twilight_validate/
request.rs

1//! Contains all other input validation functions.
2//!
3//! These functions are generally not related to a specific Discord model.
4
5use std::{
6    error::Error,
7    fmt::{Display, Formatter, Result as FmtResult},
8    time::{SystemTime, UNIX_EPOCH},
9};
10use twilight_model::id::marker::{ChannelMarker, RoleMarker};
11use twilight_model::id::Id;
12use twilight_model::util::Timestamp;
13
14/// The maximum audit log reason length in UTF-16 codepoints.
15pub const AUDIT_REASON_MAX: usize = 512;
16
17/// Maximum length of an auto moderation block action's custom message.
18pub const AUTO_MODERATION_ACTION_BLOCK_CUSTOM_MESSAGE_LENGTH_MAX: usize = 150;
19
20/// Maximum amount of mentions that triggers an auto moderation action.
21pub const AUTO_MODERATION_METADATA_MENTION_TOTAL_LIMIT: u8 = 50;
22
23/// Maximum amount of keywords allowed in the keyword filter.
24pub const AUTO_MODERATION_METADATA_KEYWORD_FILTER_MAX: usize = 1000;
25
26/// Maximum length of a keyword in the keyword filter.
27pub const AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX: usize = 60;
28
29/// Maximum amount of allowed keywords when using the custom keywords.
30pub const AUTO_MODERATION_METADATA_KEYWORD_ALLOW_LIST_MAX: usize = 100;
31
32/// Maximum amount of allowed keywords when using a keyword preset.
33pub const AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX: usize = 1000;
34
35/// Maximum length of a keyword preset keyword.
36pub const AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_LENGTH_MAX: usize = 60;
37
38/// Maximum length of a allowed keyword.
39pub const AUTO_MODERATION_METADATA_KEYWORD_ALLOW_LIST_LENGTH_MAX: usize = 60;
40
41/// Maximum amount of regex patterns that trigger the auto moderation.
42pub const AUTO_MODERATION_METADATA_REGEX_PATTERNS_MAX: usize = 10;
43
44/// Maximum length of a regex pattern.
45pub const AUTO_MODERATION_METADATA_REGEX_PATTERNS_LENGTH_MAX: usize = 260;
46
47/// Maximum amount of seconds (`2_419_200` this is equivalent to `28` days) to disable communication for.
48pub const AUTO_MODERATION_ACTION_METADATA_DURATION_SECONDS_MAX: u32 = 2_419_200;
49
50/// Maximum amount of exempt roles for the auto moderation rule.
51pub const AUTO_MODERATION_EXEMPT_ROLES_MAX: usize = 20;
52
53/// Maximum amount of exempt channels for the auto moderation rule.
54pub const AUTO_MODERATION_EXEMPT_CHANNELS_MAX: usize = 50;
55
56/// Maximum amount of seconds (`604_800` this is equivalent to `7` days) for messages to be deleted upon ban.
57pub const CREATE_GUILD_BAN_DELETE_MESSAGE_SECONDS_MAX: u32 = 604_800;
58
59/// Maximum amount of time a member can be timed out for.
60pub const COMMUNICATION_DISABLED_MAX_DURATION: i64 = 28 * 24 * 60 * 60;
61
62/// Maximum amount of messages to get.
63pub const GET_CHANNEL_MESSAGES_LIMIT_MAX: u16 = 100;
64
65/// Minimum amount of messages to get.
66pub const GET_CHANNEL_MESSAGES_LIMIT_MIN: u16 = 1;
67
68/// Maximum amount of guilds to get.
69pub const GET_CURRENT_USER_GUILDS_LIMIT_MAX: u16 = 200;
70
71/// Minimum amount of guilds to get.
72pub const GET_CURRENT_USER_GUILDS_LIMIT_MIN: u16 = 1;
73
74/// Maximum amount of entitlements to get.
75pub const GET_ENTITLEMENTS_LIMIT_MAX: u8 = 100;
76
77/// Minimum amount of entitlements to get.
78pub const GET_ENTITLEMENTS_LIMIT_MIN: u8 = 1;
79
80/// Maximum amount of audit log entries to list.
81pub const GET_GUILD_AUDIT_LOG_LIMIT_MAX: u16 = 100;
82
83/// Minimum amount of audit log entries to list.
84pub const GET_GUILD_AUDIT_LOG_LIMIT_MIN: u16 = 1;
85
86/// Maximum amount of guild bans to list.
87pub const GET_GUILD_BANS_LIMIT_MAX: u16 = 1000;
88
89/// Maximum amount of guild members to list.
90pub const GET_GUILD_MEMBERS_LIMIT_MAX: u16 = 1000;
91
92/// Minimum amount of guild members to list.
93pub const GET_GUILD_MEMBERS_LIMIT_MIN: u16 = 1;
94
95/// Maximum amount of users to return when getting reactions.
96pub const GET_REACTIONS_LIMIT_MIN: u16 = 1;
97
98/// Minimum amount of users to return when getting reactions.
99pub const GET_REACTIONS_LIMIT_MAX: u16 = 100;
100
101/// Maximum length of a guild's name.
102pub const GUILD_NAME_LENGTH_MAX: usize = 100;
103
104/// Minimum length of a guild's name.
105pub const GUILD_NAME_LENGTH_MIN: usize = 2;
106
107/// Maximum amount of days to prune users from a guild.
108pub const GUILD_PRUNE_DAYS_MAX: u16 = 30;
109
110/// Minimum amount of days to prune users from a guild.
111pub const GUILD_PRUNE_DAYS_MIN: u16 = 1;
112
113/// Maximum length of an invite's age, in seconds.
114pub const INVITE_AGE_MAX: u32 = 604_800;
115
116/// Maximum uses of an invite, if not unlimited.
117pub const INVITE_USES_MAX: u16 = 100;
118
119/// Maximum length of a maximum.
120pub const NICKNAME_LIMIT_MAX: usize = 32;
121
122/// Minimum length of a nickname.
123pub const NICKNAME_LIMIT_MIN: usize = 1;
124
125/// Maximum length of a scheduled event's description.
126pub const SCHEDULED_EVENT_DESCRIPTION_MAX: usize = 1000;
127
128/// Minimum length of a scheduled event's description.
129pub const SCHEDULED_EVENT_DESCRIPTION_MIN: usize = 1;
130
131/// Maximum amount of scheduled event users to get.
132pub const SCHEDULED_EVENT_GET_USERS_MAX: u16 = 100;
133
134/// Minimum amount of scheduled event users to get.
135pub const SCHEDULED_EVENT_GET_USERS_MIN: u16 = 1;
136
137/// Maximum length of a scheduled event's name.
138pub const SCHEDULED_EVENT_NAME_MAX: usize = 100;
139
140/// Minimum length of a scheduled event's name.
141pub const SCHEDULED_EVENT_NAME_MIN: usize = 1;
142
143/// Maximum amount of guild members to search for.
144pub const SEARCH_GUILD_MEMBERS_LIMIT_MAX: u16 = 1000;
145
146/// Minimum amount of guild members to search for.
147pub const SEARCH_GUILD_MEMBERS_LIMIT_MIN: u16 = 1;
148
149/// Maximum stage instance topic length.
150pub const STAGE_TOPIC_LENGTH_MAX: usize = 120;
151
152/// Minimum stage instance topic length.
153pub const STAGE_TOPIC_LENGTH_MIN: usize = 1;
154
155/// Maximum length of a guild template description.
156pub const TEMPLATE_DESCRIPTION_LENGTH_MAX: usize = 120;
157
158/// Maximum length of a guild template name.
159pub const TEMPLATE_NAME_LENGTH_MAX: usize = 100;
160
161/// Minimum length of a guild template name.
162pub const TEMPLATE_NAME_LENGTH_MIN: usize = 1;
163
164/// Maximum length of a username.
165pub const USERNAME_LIMIT_MAX: usize = 32;
166
167/// Minimum length of a username.
168pub const USERNAME_LIMIT_MIN: usize = 2;
169
170/// Maximum length of a webhook username.
171pub const WEBHOOK_USERNAME_LIMIT_MAX: usize = 80;
172
173/// Minimum length of a webhook username.
174pub const WEBHOOK_USERNAME_LIMIT_MIN: usize = 2;
175
176/// Forbidden substrings in usernames.
177const USERNAME_INVALID_SUBSTRINGS: [&str; 5] = ["@", "#", ":", "```", "discord"];
178
179/// Forbidden usernames.
180const USERNAME_INVALID_STRINGS: [&str; 2] = ["everyone", "here"];
181
182/// Forbidden webhook usernames.
183const WEBHOOK_INVALID_STRINGS: [&str; 1] = ["clyde"];
184
185/// A field is not valid.
186#[derive(Debug)]
187pub struct ValidationError {
188    /// Type of error that occurred.
189    kind: ValidationErrorType,
190}
191
192impl ValidationError {
193    /// Immutable reference to the type of error that occurred.
194    #[must_use = "retrieving the type has no effect if left unused"]
195    pub const fn kind(&self) -> &ValidationErrorType {
196        &self.kind
197    }
198
199    /// Consume the error, returning the source error if there is any.
200    #[allow(clippy::unused_self)]
201    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
202    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
203        None
204    }
205
206    /// Consume the error, returning the owned error type and the source error.
207    #[must_use = "consuming the error into its parts has no effect if left unused"]
208    pub fn into_parts(self) -> (ValidationErrorType, Option<Box<dyn Error + Send + Sync>>) {
209        (self.kind, None)
210    }
211}
212
213impl Display for ValidationError {
214    #[allow(clippy::too_many_lines)]
215    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
216        match &self.kind {
217            ValidationErrorType::AuditReason { len } => {
218                f.write_str("provided audit reason length is ")?;
219                Display::fmt(len, f)?;
220                f.write_str(", but it must be at most ")?;
221
222                Display::fmt(&AUDIT_REASON_MAX, f)
223            }
224            ValidationErrorType::AutoModerationBlockActionCustomMessageLimit { len } => {
225                f.write_str("provided auto moderation block action custom message length is ")?;
226                Display::fmt(len, f)?;
227                f.write_str(", but it must be at most ")?;
228
229                Display::fmt(&AUTO_MODERATION_ACTION_BLOCK_CUSTOM_MESSAGE_LENGTH_MAX, f)
230            }
231            ValidationErrorType::AutoModerationMetadataMentionTotalLimit { limit } => {
232                f.write_str("provided auto moderation metadata mention_total_limit is ")?;
233                Display::fmt(limit, f)?;
234                f.write_str(", but it must be at most ")?;
235
236                Display::fmt(&AUTO_MODERATION_METADATA_MENTION_TOTAL_LIMIT, f)
237            }
238            ValidationErrorType::AutoModerationMetadataAllowList { len } => {
239                f.write_str("provided auto moderation metadata allow_list length is ")?;
240                Display::fmt(len, f)?;
241                f.write_str(", but it must be at most ")?;
242
243                Display::fmt(&AUTO_MODERATION_METADATA_KEYWORD_ALLOW_LIST_MAX, f)
244            }
245            ValidationErrorType::AutoModerationMetadataAllowListItem { len, substring } => {
246                f.write_str("provided auto moderation metadata allow_list item ")?;
247                Display::fmt(substring, f)?;
248                f.write_str(" length is ")?;
249                Display::fmt(len, f)?;
250                f.write_str(", but it must be at most ")?;
251
252                Display::fmt(&AUTO_MODERATION_METADATA_KEYWORD_ALLOW_LIST_LENGTH_MAX, f)
253            }
254            ValidationErrorType::AutoModerationMetadataKeywordFilter { len } => {
255                f.write_str("provided auto moderation metadata keyword_filter length is ")?;
256                Display::fmt(len, f)?;
257                f.write_str(", but it must be at most ")?;
258
259                Display::fmt(&AUTO_MODERATION_METADATA_KEYWORD_FILTER_MAX, f)
260            }
261            ValidationErrorType::AutoModerationMetadataKeywordFilterItem { len, substring } => {
262                f.write_str("provided auto moderation metadata keyword_filter item ")?;
263                Display::fmt(substring, f)?;
264                f.write_str(" length is ")?;
265                Display::fmt(len, f)?;
266                f.write_str(", but it must be at most ")?;
267
268                Display::fmt(&AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX, f)
269            }
270            ValidationErrorType::AutoModerationMetadataPresetAllowList { len } => {
271                f.write_str("provided auto moderation metadata allow_list length is ")?;
272                Display::fmt(len, f)?;
273                f.write_str(", but it must be at most ")?;
274
275                Display::fmt(&AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX, f)
276            }
277            ValidationErrorType::AutoModerationMetadataPresetAllowListItem { len, substring } => {
278                f.write_str("provided auto moderation metadata allow_list item ")?;
279                Display::fmt(substring, f)?;
280                f.write_str(" length is ")?;
281                Display::fmt(len, f)?;
282                f.write_str(", but it must be at most ")?;
283
284                Display::fmt(&AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_LENGTH_MAX, f)
285            }
286            ValidationErrorType::AutoModerationActionMetadataDurationSeconds { seconds } => {
287                f.write_str("provided auto moderation action timeout duration is ")?;
288                Display::fmt(seconds, f)?;
289                f.write_str(", but it must be at most ")?;
290
291                Display::fmt(&AUTO_MODERATION_ACTION_METADATA_DURATION_SECONDS_MAX, f)
292            }
293            ValidationErrorType::AutoModerationMetadataRegexPatterns { len } => {
294                f.write_str("provided auto moderation metadata regex_patterns length is ")?;
295                Display::fmt(len, f)?;
296                f.write_str(", but it must be at most ")?;
297
298                Display::fmt(&AUTO_MODERATION_METADATA_REGEX_PATTERNS_MAX, f)
299            }
300            ValidationErrorType::AutoModerationMetadataRegexPatternsItem { len, substring } => {
301                f.write_str("provided auto moderation metadata regex_patterns item ")?;
302                Display::fmt(substring, f)?;
303                f.write_str(" length is ")?;
304                Display::fmt(len, f)?;
305                f.write_str(", but it must be at most ")?;
306
307                Display::fmt(&AUTO_MODERATION_METADATA_REGEX_PATTERNS_LENGTH_MAX, f)
308            }
309            ValidationErrorType::AutoModerationExemptRoles { len } => {
310                f.write_str("provided auto moderation exempt_roles length is ")?;
311                Display::fmt(len, f)?;
312                f.write_str(", but it must be at most ")?;
313
314                Display::fmt(&AUTO_MODERATION_EXEMPT_ROLES_MAX, f)
315            }
316            ValidationErrorType::AutoModerationExemptChannels { len } => {
317                f.write_str("provided auto moderation exempt_channels length is ")?;
318                Display::fmt(len, f)?;
319                f.write_str(", but it must be at most ")?;
320
321                Display::fmt(&AUTO_MODERATION_EXEMPT_CHANNELS_MAX, f)
322            }
323            ValidationErrorType::CreateGuildBanDeleteMessageSeconds {
324                seconds: delete_message_seconds,
325            } => {
326                f.write_str("provided create guild ban delete_message_seconds is ")?;
327                Display::fmt(delete_message_seconds, f)?;
328                f.write_str(", but it must be at most ")?;
329
330                Display::fmt(&CREATE_GUILD_BAN_DELETE_MESSAGE_SECONDS_MAX, f)
331            }
332            ValidationErrorType::CommunicationDisabledUntil { .. } => {
333                f.write_str("provided timestamp is too far in the future")
334            }
335            ValidationErrorType::GetChannelMessages { limit } => {
336                f.write_str("provided get guild members limit is ")?;
337                Display::fmt(limit, f)?;
338                f.write_str(", but it must be at least ")?;
339                Display::fmt(&GET_CHANNEL_MESSAGES_LIMIT_MIN, f)?;
340                f.write_str(" and at most ")?;
341
342                Display::fmt(&GET_CHANNEL_MESSAGES_LIMIT_MAX, f)
343            }
344            ValidationErrorType::GetCurrentUserGuilds { limit } => {
345                f.write_str("provided get current user guilds limit is ")?;
346                Display::fmt(limit, f)?;
347                f.write_str(", but it must be at least ")?;
348                Display::fmt(&GET_CURRENT_USER_GUILDS_LIMIT_MIN, f)?;
349                f.write_str(" and at most ")?;
350
351                Display::fmt(&GET_CURRENT_USER_GUILDS_LIMIT_MAX, f)
352            }
353            ValidationErrorType::GetEntitlements { limit } => {
354                f.write_str("provided get entitlements limit is ")?;
355                Display::fmt(limit, f)?;
356                f.write_str(", but it must be at least ")?;
357                Display::fmt(&GET_ENTITLEMENTS_LIMIT_MIN, f)?;
358                f.write_str(" and at most ")?;
359
360                Display::fmt(&GET_ENTITLEMENTS_LIMIT_MAX, f)
361            }
362            ValidationErrorType::GetGuildAuditLog { limit } => {
363                f.write_str("provided get guild audit log limit is ")?;
364                Display::fmt(limit, f)?;
365                f.write_str(", but it must be at least ")?;
366                Display::fmt(&GET_GUILD_MEMBERS_LIMIT_MIN, f)?;
367                f.write_str(" and at most ")?;
368
369                Display::fmt(&GET_GUILD_MEMBERS_LIMIT_MAX, f)
370            }
371            ValidationErrorType::GetGuildBans { limit } => {
372                f.write_str("provided get guild bans limit is ")?;
373                Display::fmt(limit, f)?;
374                f.write_str(", but it must be at most ")?;
375
376                Display::fmt(&GET_GUILD_BANS_LIMIT_MAX, f)
377            }
378            ValidationErrorType::GetGuildMembers { limit } => {
379                f.write_str("provided get guild members limit is ")?;
380                Display::fmt(limit, f)?;
381                f.write_str(", but it must be at least ")?;
382                Display::fmt(&GET_GUILD_MEMBERS_LIMIT_MIN, f)?;
383                f.write_str(" and at most ")?;
384
385                Display::fmt(&GET_GUILD_MEMBERS_LIMIT_MAX, f)
386            }
387            ValidationErrorType::GetReactions { limit } => {
388                f.write_str("provided get reactions limit is ")?;
389                Display::fmt(limit, f)?;
390                f.write_str(", but it must be at least ")?;
391                Display::fmt(&GET_REACTIONS_LIMIT_MIN, f)?;
392                f.write_str(" and at most ")?;
393
394                Display::fmt(&GET_REACTIONS_LIMIT_MAX, f)
395            }
396            ValidationErrorType::GuildName { len } => {
397                f.write_str("provided guild name length is ")?;
398                Display::fmt(len, f)?;
399                f.write_str(", but it must be at least ")?;
400                Display::fmt(&GUILD_NAME_LENGTH_MIN, f)?;
401                f.write_str(" and at most ")?;
402
403                Display::fmt(&GUILD_NAME_LENGTH_MAX, f)
404            }
405            ValidationErrorType::GuildPruneDays { days } => {
406                f.write_str("provided prune days is ")?;
407                Display::fmt(days, f)?;
408                f.write_str(", but it must be at least ")?;
409                Display::fmt(&GUILD_PRUNE_DAYS_MIN, f)?;
410                f.write_str(" and at most ")?;
411
412                Display::fmt(&GUILD_PRUNE_DAYS_MAX, f)
413            }
414            ValidationErrorType::InviteMaxAge { max_age } => {
415                f.write_str("provided invite max_age is ")?;
416                Display::fmt(max_age, f)?;
417                f.write_str(", but it must be at most ")?;
418
419                Display::fmt(&INVITE_AGE_MAX, f)
420            }
421            ValidationErrorType::InviteMaxUses { max_uses } => {
422                f.write_str("provided invite max_uses is ")?;
423                Display::fmt(max_uses, f)?;
424                f.write_str(", but it must be at most ")?;
425
426                Display::fmt(&INVITE_USES_MAX, f)
427            }
428            ValidationErrorType::Nickname { len } => {
429                f.write_str("provided nickname length is ")?;
430                Display::fmt(len, f)?;
431                f.write_str(", but it must be at least ")?;
432                Display::fmt(&NICKNAME_LIMIT_MIN, f)?;
433                f.write_str(" and at most ")?;
434
435                Display::fmt(&NICKNAME_LIMIT_MAX, f)
436            }
437            ValidationErrorType::ScheduledEventDescription { len } => {
438                f.write_str("provided scheduled event description is length is ")?;
439                Display::fmt(len, f)?;
440                f.write_str(", but it must be at least ")?;
441                Display::fmt(&SCHEDULED_EVENT_DESCRIPTION_MIN, f)?;
442                f.write_str(" and at most ")?;
443
444                Display::fmt(&SCHEDULED_EVENT_DESCRIPTION_MAX, f)
445            }
446            ValidationErrorType::ScheduledEventGetUsers { limit } => {
447                f.write_str("provided scheduled event get users limit is ")?;
448                Display::fmt(limit, f)?;
449                f.write_str(", but it must be at least ")?;
450                Display::fmt(&SCHEDULED_EVENT_GET_USERS_MIN, f)?;
451                f.write_str(" and at most ")?;
452
453                Display::fmt(&SCHEDULED_EVENT_GET_USERS_MAX, f)
454            }
455            ValidationErrorType::ScheduledEventName { len } => {
456                f.write_str("provided scheduled event name is length is ")?;
457                Display::fmt(len, f)?;
458                f.write_str(", but it must be at least ")?;
459                Display::fmt(&SCHEDULED_EVENT_NAME_MIN, f)?;
460                f.write_str(" and at most ")?;
461
462                Display::fmt(&SCHEDULED_EVENT_NAME_MAX, f)
463            }
464            ValidationErrorType::SearchGuildMembers { limit } => {
465                f.write_str("provided search guild members limit is ")?;
466                Display::fmt(limit, f)?;
467                f.write_str(", but it must be at least ")?;
468                Display::fmt(&SEARCH_GUILD_MEMBERS_LIMIT_MIN, f)?;
469                f.write_str(" and at most ")?;
470
471                Display::fmt(&SEARCH_GUILD_MEMBERS_LIMIT_MAX, f)
472            }
473            ValidationErrorType::StageTopic { len } => {
474                f.write_str("provided stage instance topic length is ")?;
475                Display::fmt(len, f)?;
476                f.write_str(", but it must be at least ")?;
477                Display::fmt(&STAGE_TOPIC_LENGTH_MIN, f)?;
478                f.write_str(" and at most ")?;
479
480                Display::fmt(&STAGE_TOPIC_LENGTH_MAX, f)
481            }
482            ValidationErrorType::TemplateDescription { len } => {
483                f.write_str("provided guild template description topic length is ")?;
484                Display::fmt(len, f)?;
485                f.write_str(", but it must be at most ")?;
486
487                Display::fmt(&TEMPLATE_DESCRIPTION_LENGTH_MAX, f)
488            }
489            ValidationErrorType::TemplateName { len } => {
490                f.write_str("provided guild template name length is ")?;
491                Display::fmt(len, f)?;
492                f.write_str(", but it must be at least ")?;
493                Display::fmt(&TEMPLATE_NAME_LENGTH_MIN, f)?;
494                f.write_str(" and at most ")?;
495
496                Display::fmt(&TEMPLATE_NAME_LENGTH_MAX, f)
497            }
498            ValidationErrorType::Username { len, substring }
499            | ValidationErrorType::WebhookUsername { len, substring } => {
500                f.write_str("provided username")?;
501
502                if let Some(len) = len {
503                    f.write_str(" length is ")?;
504                    Display::fmt(len, f)?;
505                    f.write_str(", but it must be at least ")?;
506                    Display::fmt(&USERNAME_LIMIT_MIN, f)?;
507                    f.write_str(" and at most ")?;
508                    Display::fmt(&USERNAME_LIMIT_MAX, f)?;
509                }
510
511                if let Some(substring) = substring {
512                    if len.is_some() {
513                        f.write_str(", and")?;
514                    }
515
516                    f.write_str(" cannot contain ")?;
517                    Display::fmt(substring, f)?;
518                }
519
520                Ok(())
521            }
522        }
523    }
524}
525
526impl Error for ValidationError {}
527
528/// Type of [`ValidationError`] that occurred.
529#[derive(Debug)]
530pub enum ValidationErrorType {
531    /// Provided audit reason was too large.
532    AuditReason {
533        /// Invalid length.
534        len: usize,
535    },
536    /// Provided block action custom message was too long.
537    AutoModerationBlockActionCustomMessageLimit {
538        /// Invalid limit.
539        len: usize,
540    },
541    /// Provided limit was too large.
542    AutoModerationMetadataMentionTotalLimit {
543        /// Invalid limit.
544        limit: u8,
545    },
546    /// Provided keyword filter was invalid.
547    AutoModerationMetadataKeywordFilter {
548        /// Invalid length.
549        len: usize,
550    },
551    /// Provided keyword was invalid.
552    AutoModerationMetadataKeywordFilterItem {
553        /// Invalid length.
554        len: usize,
555        /// Invalid substring.
556        substring: String,
557    },
558    /// Provided keyword allow list was invalid.
559    AutoModerationMetadataAllowList {
560        /// Invalid length.
561        len: usize,
562    },
563    /// Provided allow list item was invalid.
564    AutoModerationMetadataAllowListItem {
565        /// Invalid length.
566        len: usize,
567        /// Invalid substring.
568        substring: String,
569    },
570    /// Provided keyword preset allow list was invalid.
571    AutoModerationMetadataPresetAllowList {
572        /// Invalid length.
573        len: usize,
574    },
575    /// Provided keyword preset allow list item was invalid.
576    AutoModerationMetadataPresetAllowListItem {
577        /// Invalid length.
578        len: usize,
579        /// Invalid substring.
580        substring: String,
581    },
582    /// Provided seconds to disable communication was invalid.
583    AutoModerationActionMetadataDurationSeconds {
584        /// Invalid seconds.
585        seconds: u32,
586    },
587    /// Provided regex patterns was invalid.
588    AutoModerationMetadataRegexPatterns {
589        /// Invalid length.
590        len: usize,
591    },
592    /// Provided regex patterns item was invalid.
593    AutoModerationMetadataRegexPatternsItem {
594        /// Invalid length.
595        len: usize,
596        /// Invalid substring.
597        substring: String,
598    },
599    /// Provided exempt roles was invalid.
600    AutoModerationExemptRoles {
601        /// Invalid length.
602        len: usize,
603    },
604    /// Provided exempt channels was invalid.
605    AutoModerationExemptChannels {
606        /// Invalid length.
607        len: usize,
608    },
609    /// Provided create guild ban delete message seconds was invalid.
610    CreateGuildBanDeleteMessageSeconds {
611        /// Invalid seconds.
612        seconds: u32,
613    },
614    /// Provided timestamp is too far in the future.
615    CommunicationDisabledUntil {
616        /// Invalid timestamp.
617        timestamp: Timestamp,
618    },
619    /// Provided get channel messages limit was invalid.
620    GetChannelMessages {
621        /// Invalid limit.
622        limit: u16,
623    },
624    /// Provided get current user guilds limit was invalid.
625    GetCurrentUserGuilds {
626        /// Invalid limit.
627        limit: u16,
628    },
629    /// Provided get entitlements limit was invalid.
630    GetEntitlements {
631        /// Invalid limit.
632        limit: u8,
633    },
634    /// Provided get guild audit log limit was invalid.
635    GetGuildAuditLog {
636        /// Invalid limit.
637        limit: u16,
638    },
639    /// Provided get guild bans limit was invalid.
640    GetGuildBans {
641        /// Invalid limit.
642        limit: u16,
643    },
644    /// Provided get guild members limit was invalid.
645    GetGuildMembers {
646        /// Invalid limit.
647        limit: u16,
648    },
649    /// Provided get reactions limit was invalid.
650    GetReactions {
651        /// Invalid limit.
652        limit: u16,
653    },
654    /// Provided guild name was invalid.
655    GuildName {
656        /// Invalid length.
657        len: usize,
658    },
659    /// Provided guild prune days was invalid.
660    GuildPruneDays {
661        /// Invalid days.
662        days: u16,
663    },
664    /// Provided invite max age was invalid.
665    InviteMaxAge {
666        /// Invalid age.
667        max_age: u32,
668    },
669    /// Provided invite max uses was invalid.
670    InviteMaxUses {
671        /// Invalid age.
672        max_uses: u16,
673    },
674    /// Provided nickname length was invalid.
675    Nickname {
676        /// Invalid length.
677        len: usize,
678    },
679    /// Scheduled event description is invalid.
680    ScheduledEventDescription {
681        /// Invalid length.
682        len: usize,
683    },
684    /// Scheduled event get users limit is invalid.
685    ScheduledEventGetUsers {
686        /// Invalid limit.
687        limit: u16,
688    },
689    /// Scheduled event name is invalid.
690    ScheduledEventName {
691        /// Invalid length.
692        len: usize,
693    },
694    /// Provided search guild members limit was invalid.
695    SearchGuildMembers {
696        /// Invalid limit.
697        limit: u16,
698    },
699    /// Provided stage instance topic was invalid.
700    StageTopic {
701        /// Invalid length.
702        len: usize,
703    },
704    /// Provided guild template description was invalid.
705    TemplateDescription {
706        /// Invalid length.
707        len: usize,
708    },
709    /// Provided guild template name was invalid.
710    TemplateName {
711        /// Invalid length.
712        len: usize,
713    },
714    /// Provided username was invalid.
715    Username {
716        /// Invalid length.
717        len: Option<usize>,
718        /// Invalid substring.
719        substring: Option<&'static str>,
720    },
721    /// Provided webhook username was invalid.
722    WebhookUsername {
723        /// Invalid length.
724        len: Option<usize>,
725        /// Invalid substring.
726        substring: Option<&'static str>,
727    },
728}
729
730/// Ensure that an audit reason is correct.
731///
732/// The length must be at most [`AUDIT_REASON_MAX`]. This is based on
733/// [this documentation entry].
734///
735/// # Errors
736///
737/// Returns an error of type [`AuditReason`] if the length is invalid.
738///
739/// [`AuditReason`]: ValidationErrorType::AuditReason
740/// [this documentation entry]: https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-entry-structure
741pub fn audit_reason(audit_reason: impl AsRef<str>) -> Result<(), ValidationError> {
742    let len = audit_reason.as_ref().chars().count();
743
744    if len <= AUDIT_REASON_MAX {
745        Ok(())
746    } else {
747        Err(ValidationError {
748            kind: ValidationErrorType::AuditReason { len },
749        })
750    }
751}
752
753/// Ensure that an auto moderation block action's `custom_message` is correct.
754///
755/// The length must be at most [`AUTO_MODERATION_ACTION_BLOCK_CUSTOM_MESSAGE_LENGTH_MAX`].
756/// This is based on [this documentation entry].
757///
758/// # Errors
759///
760/// Returns an error of type [`AutoModerationBlockActionCustomMessageLimit`] if the
761/// length is invalid.
762///
763/// [`AutoModerationBlockActionCustomMessageLimit`]: ValidationErrorType::AutoModerationBlockActionCustomMessageLimit
764/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
765pub fn auto_moderation_block_action_custom_message_limit(
766    custom_message: impl AsRef<str>,
767) -> Result<(), ValidationError> {
768    let len = custom_message.as_ref().chars().count();
769
770    if len <= AUTO_MODERATION_ACTION_BLOCK_CUSTOM_MESSAGE_LENGTH_MAX {
771        Ok(())
772    } else {
773        Err(ValidationError {
774            kind: ValidationErrorType::AutoModerationBlockActionCustomMessageLimit { len },
775        })
776    }
777}
778
779/// Ensure that an auto moderation rule's `mention_total_limit` is correct.
780///
781/// The length must be at most [`AUTO_MODERATION_METADATA_MENTION_TOTAL_LIMIT`].
782/// This is based on [this documentation entry].
783///
784/// # Errors
785///
786/// Returns an error of type [`AutoModerationMetadataMentionTotalLimit`] if the
787/// length is invalid.
788///
789/// [`AutoModerationMetadataMentionTotalLimit`]: ValidationErrorType::AutoModerationMetadataMentionTotalLimit
790/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata
791pub const fn auto_moderation_metadata_mention_total_limit(
792    limit: u8,
793) -> Result<(), ValidationError> {
794    if limit <= AUTO_MODERATION_METADATA_MENTION_TOTAL_LIMIT {
795        Ok(())
796    } else {
797        Err(ValidationError {
798            kind: ValidationErrorType::AutoModerationMetadataMentionTotalLimit { limit },
799        })
800    }
801}
802
803/// Ensure that an auto moderation rule's `keyword_filter` is correct.
804///
805/// The length must be at most [`AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX`].
806/// This is based on [this documentation entry].
807///
808/// # Errors
809///
810/// Returns an error of type [`AutoModerationMetadataKeywordFilter`] if the
811/// length is invalid.
812///
813/// Returns an error of type [`AutoModerationMetadataKeywordFilterItem`] if any
814/// of the keywords are invalid.
815///
816/// [`AutoModerationMetadataKeywordFilter`]: ValidationErrorType::AutoModerationMetadataKeywordFilter
817/// [`AutoModerationMetadataKeywordFilterItem`]: ValidationErrorType::AutoModerationMetadataKeywordFilterItem
818/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
819pub fn auto_moderation_metadata_keyword_filter(
820    keywords: &[impl AsRef<str>],
821) -> Result<(), ValidationError> {
822    let len = keywords.len();
823
824    if len <= AUTO_MODERATION_METADATA_KEYWORD_FILTER_MAX {
825        for keyword in keywords {
826            auto_moderation_metadata_keyword_filter_item(keyword)?;
827        }
828
829        Ok(())
830    } else {
831        Err(ValidationError {
832            kind: ValidationErrorType::AutoModerationMetadataKeywordFilter { len },
833        })
834    }
835}
836
837/// Ensure that an auto moderation rule's `keyword_filter` item is correct.
838///
839/// The length must be at most [`AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX`].
840/// This is based on [this documentation entry].
841///
842/// # Errors
843///
844/// Returns an error of type [`AutoModerationMetadataKeywordFilterItem`] if the
845/// length is invalid.
846///
847/// [`AutoModerationMetadataKeywordFilterItem`]: ValidationErrorType::AutoModerationMetadataKeywordFilterItem
848/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
849pub fn auto_moderation_metadata_keyword_filter_item(
850    keyword: impl AsRef<str>,
851) -> Result<(), ValidationError> {
852    let len = keyword.as_ref().chars().count();
853
854    if len <= AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX {
855        Ok(())
856    } else {
857        Err(ValidationError {
858            kind: ValidationErrorType::AutoModerationMetadataKeywordFilterItem {
859                len,
860                substring: keyword.as_ref().to_string(),
861            },
862        })
863    }
864}
865
866/// Ensure that an auto moderation rule's `allow_list` is correct.
867///
868/// The length must be at most [`AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX`].
869/// This is based on [this documentation entry].
870///
871/// # Errors
872///
873/// Returns an error of type [`AutoModerationMetadataAllowList`] if the
874/// length is invalid.
875///
876/// Returns an error of type [`AutoModerationMetadataAllowListItem`] if any of
877/// the items are invalid.
878///
879/// [`AutoModerationMetadataAllowList`]: ValidationErrorType::AutoModerationMetadataAllowList
880/// [`AutoModerationMetadataAllowListItem`]: ValidationErrorType::AutoModerationMetadataAllowListItem
881/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
882pub fn auto_moderation_metadata_keyword_allow_list(
883    keywords: &[impl AsRef<str>],
884) -> Result<(), ValidationError> {
885    let len = keywords.len();
886
887    if len <= AUTO_MODERATION_METADATA_KEYWORD_ALLOW_LIST_MAX {
888        for keyword in keywords {
889            auto_moderation_metadata_keyword_allow_list_item(keyword)?;
890        }
891
892        Ok(())
893    } else {
894        Err(ValidationError {
895            kind: ValidationErrorType::AutoModerationMetadataAllowList { len },
896        })
897    }
898}
899
900/// Ensure that an auto moderation rule's `allow_list` item is correct.
901///
902/// The length must be at most [`AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX`].
903/// This is based on [this documentation entry].
904///
905/// # Errors
906///
907/// Returns an error of type [`AutoModerationMetadataAllowListItem`] if the
908/// length is invalid.
909///
910/// [`AutoModerationMetadataAllowListItem`]: ValidationErrorType::AutoModerationMetadataAllowListItem
911/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
912pub fn auto_moderation_metadata_keyword_allow_list_item(
913    keyword: impl AsRef<str>,
914) -> Result<(), ValidationError> {
915    let len = keyword.as_ref().chars().count();
916
917    if len <= AUTO_MODERATION_METADATA_KEYWORD_FILTER_LENGTH_MAX {
918        Ok(())
919    } else {
920        Err(ValidationError {
921            kind: ValidationErrorType::AutoModerationMetadataAllowListItem {
922                len,
923                substring: keyword.as_ref().to_string(),
924            },
925        })
926    }
927}
928
929/// Ensure that an auto moderation rule's `allow_list` is correct when using presets.
930///
931/// The length must be at most [`AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX`].
932/// This is based on [this documentation entry].
933///
934/// # Errors
935///
936/// Returns an error of type [`AutoModerationMetadataPresetAllowList`] if the
937/// length is invalid.
938///
939/// Returns an error of type [`AutoModerationMetadataPresetAllowListItem`] if any of
940/// the items are invalid.
941///
942/// [`AutoModerationMetadataPresetAllowList`]: ValidationErrorType::AutoModerationMetadataPresetAllowList
943/// [`AutoModerationMetadataPresetAllowListItem`]: ValidationErrorType::AutoModerationMetadataPresetAllowListItem
944/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
945pub fn auto_moderation_metadata_preset_allow_list(
946    keywords: &[impl AsRef<str>],
947) -> Result<(), ValidationError> {
948    let len = keywords.len();
949
950    if len <= AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_MAX {
951        for keyword in keywords {
952            auto_moderation_metadata_preset_allow_list_item(keyword)?;
953        }
954
955        Ok(())
956    } else {
957        Err(ValidationError {
958            kind: ValidationErrorType::AutoModerationMetadataPresetAllowList { len },
959        })
960    }
961}
962
963/// Ensure that an auto moderation rule's `allow_list` item is correct when using presets.
964///
965/// The length must be at most [`AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_LENGTH_MAX`].
966/// This is based on [this documentation entry].
967///
968/// # Errors
969///
970/// Returns an error of type [`AutoModerationMetadataPresetAllowListItem`] if the
971/// length is invalid.
972///
973/// [`AutoModerationMetadataPresetAllowListItem`]: ValidationErrorType::AutoModerationMetadataPresetAllowListItem
974/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
975pub fn auto_moderation_metadata_preset_allow_list_item(
976    keyword: impl AsRef<str>,
977) -> Result<(), ValidationError> {
978    let len = keyword.as_ref().chars().count();
979
980    if len <= AUTO_MODERATION_METADATA_PRESET_ALLOW_LIST_LENGTH_MAX {
981        Ok(())
982    } else {
983        Err(ValidationError {
984            kind: ValidationErrorType::AutoModerationMetadataPresetAllowListItem {
985                len,
986                substring: keyword.as_ref().to_string(),
987            },
988        })
989    }
990}
991
992/// Ensure that an auto moderation rule's `regex_patterns` is correct.
993///
994/// The length must be at most [`AUTO_MODERATION_METADATA_REGEX_PATTERNS_MAX`].
995/// This is based on [this documentation entry].
996///
997/// # Errors
998///
999/// Returns an error of type [`AutoModerationMetadataRegexPatterns`] if the
1000/// length is invalid.
1001///
1002/// Returns an error of type [`AutoModerationMetadataRegexPatternsItem`] if any of
1003/// the items are invalid.
1004///
1005/// [`AutoModerationMetadataRegexPatterns`]: ValidationErrorType::AutoModerationMetadataRegexPatterns
1006/// [`AutoModerationMetadataRegexPatternsItem`]: ValidationErrorType::AutoModerationMetadataRegexPatternsItem
1007/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
1008pub fn auto_moderation_metadata_regex_patterns(
1009    patterns: &[impl AsRef<str>],
1010) -> Result<(), ValidationError> {
1011    let len = patterns.len();
1012
1013    if len <= AUTO_MODERATION_METADATA_REGEX_PATTERNS_MAX {
1014        for pattern in patterns {
1015            auto_moderation_metadata_regex_patterns_item(pattern)?;
1016        }
1017
1018        Ok(())
1019    } else {
1020        Err(ValidationError {
1021            kind: ValidationErrorType::AutoModerationMetadataRegexPatterns { len },
1022        })
1023    }
1024}
1025
1026/// Ensure that an auto moderation rule's `regex_patterns` item is correct.
1027///
1028/// The length must be at most [`AUTO_MODERATION_METADATA_REGEX_PATTERNS_LENGTH_MAX`].
1029/// This is based on [this documentation entry].
1030///
1031/// # Errors
1032///
1033/// Returns an error of type [`AutoModerationMetadataRegexPatternsItem`] if the
1034/// length is invalid.
1035///
1036/// [`AutoModerationMetadataRegexPatternsItem`]: ValidationErrorType::AutoModerationMetadataRegexPatternsItem
1037/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata-field-limits
1038pub fn auto_moderation_metadata_regex_patterns_item(
1039    pattern: impl AsRef<str>,
1040) -> Result<(), ValidationError> {
1041    let len = pattern.as_ref().chars().count();
1042
1043    if len <= AUTO_MODERATION_METADATA_REGEX_PATTERNS_LENGTH_MAX {
1044        Ok(())
1045    } else {
1046        Err(ValidationError {
1047            kind: ValidationErrorType::AutoModerationMetadataRegexPatternsItem {
1048                len,
1049                substring: pattern.as_ref().to_string(),
1050            },
1051        })
1052    }
1053}
1054
1055/// Ensure that the `duration_seconds` field for an auto moderation action
1056/// metadata is correct.
1057///
1058/// The duration must be at most [`AUTO_MODERATION_ACTION_METADATA_DURATION_SECONDS_MAX`].
1059/// This is based on [this documentation entry].
1060///
1061/// # Errors
1062///
1063/// Returns an error of type [`AutoModerationActionMetadataDurationSeconds`] if the
1064/// duration is invalid.
1065///
1066/// [`AutoModerationActionMetadataDurationSeconds`]: ValidationErrorType::AutoModerationActionMetadataDurationSeconds
1067/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
1068pub const fn auto_moderation_action_metadata_duration_seconds(
1069    seconds: u32,
1070) -> Result<(), ValidationError> {
1071    if seconds <= AUTO_MODERATION_ACTION_METADATA_DURATION_SECONDS_MAX {
1072        Ok(())
1073    } else {
1074        Err(ValidationError {
1075            kind: ValidationErrorType::AutoModerationActionMetadataDurationSeconds { seconds },
1076        })
1077    }
1078}
1079
1080/// Ensure that the `exempt_roles` field for an auto moderation rule is correct.
1081///
1082/// The length must be at most [`AUTO_MODERATION_EXEMPT_ROLES_MAX`]. This is based
1083/// on [this documentation entry].
1084///
1085/// # Errors
1086///
1087/// Returns an error of type [`AutoModerationExemptRoles`] if the length is
1088/// invalid.
1089///
1090/// [`AutoModerationExemptRoles`]: ValidationErrorType::AutoModerationExemptRoles
1091/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-auto-moderation-rule-structure
1092pub const fn auto_moderation_exempt_roles(roles: &[Id<RoleMarker>]) -> Result<(), ValidationError> {
1093    let len = roles.len();
1094
1095    if len <= AUTO_MODERATION_EXEMPT_ROLES_MAX {
1096        Ok(())
1097    } else {
1098        Err(ValidationError {
1099            kind: ValidationErrorType::AutoModerationExemptRoles { len },
1100        })
1101    }
1102}
1103
1104/// Ensure that the `exempt_channels` field for an auto moderation rule is correct.
1105///
1106/// The length must be at most [`AUTO_MODERATION_EXEMPT_CHANNELS_MAX`]. This is based
1107/// on [this documentation entry].
1108///
1109/// # Errors
1110///
1111/// Returns an error of type [`AutoModerationExemptChannels`] if the length is
1112/// invalid.
1113///
1114/// [`AutoModerationExemptChannels`]: ValidationErrorType::AutoModerationExemptChannels
1115/// [this documentation entry]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-auto-moderation-rule-structure
1116pub const fn auto_moderation_exempt_channels(
1117    channels: &[Id<ChannelMarker>],
1118) -> Result<(), ValidationError> {
1119    let len = channels.len();
1120
1121    if len <= AUTO_MODERATION_EXEMPT_CHANNELS_MAX {
1122        Ok(())
1123    } else {
1124        Err(ValidationError {
1125            kind: ValidationErrorType::AutoModerationExemptChannels { len },
1126        })
1127    }
1128}
1129
1130/// Ensure that the delete message seconds amount for the Create Guild Ban request
1131/// is correct.
1132///
1133/// The seconds must be at most [`CREATE_GUILD_BAN_DELETE_MESSAGE_SECONDS_MAX`]. This
1134/// is based on [this documentation entry].
1135///
1136/// # Errors
1137///
1138/// Returns an error of type [`CreateGuildBanDeleteMessageSeconds`] if the seconds is
1139/// invalid.
1140///
1141/// [`CreateGuildBanDeleteMessageSeconds`]: ValidationErrorType::CreateGuildBanDeleteMessageSeconds
1142/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#create-guild-ban
1143pub const fn create_guild_ban_delete_message_seconds(seconds: u32) -> Result<(), ValidationError> {
1144    if seconds <= CREATE_GUILD_BAN_DELETE_MESSAGE_SECONDS_MAX {
1145        Ok(())
1146    } else {
1147        Err(ValidationError {
1148            kind: ValidationErrorType::CreateGuildBanDeleteMessageSeconds { seconds },
1149        })
1150    }
1151}
1152
1153/// Validate that a timeout time is not too far in the future.
1154///
1155/// The time must not be farther than 28 days in the future.
1156///
1157/// # Errors
1158#[allow(clippy::cast_possible_wrap)] // casting of unix timestamp should never wrap
1159pub fn communication_disabled_until(timestamp: Timestamp) -> Result<(), ValidationError> {
1160    let now = SystemTime::now()
1161        .duration_since(UNIX_EPOCH)
1162        .map_err(|_| ValidationError {
1163            kind: ValidationErrorType::CommunicationDisabledUntil { timestamp },
1164        })?;
1165
1166    let end = timestamp.as_secs();
1167
1168    if end - now.as_secs() as i64 <= COMMUNICATION_DISABLED_MAX_DURATION {
1169        Ok(())
1170    } else {
1171        Err(ValidationError {
1172            kind: ValidationErrorType::CommunicationDisabledUntil { timestamp },
1173        })
1174    }
1175}
1176
1177/// Ensure that the limit for the Get Channel Messages request is correct.
1178///
1179/// The limit must be at least [`GET_CHANNEL_MESSAGES_LIMIT_MIN`] and at most
1180/// [`GET_CHANNEL_MESSAGES_LIMIT_MAX`]. This is based on
1181/// [this documentation entry].
1182///
1183/// # Errors
1184///
1185/// Returns an error of type [`GetChannelMessages`] if the limit is invalid.
1186///
1187/// [`GetChannelMessages`]: ValidationErrorType::GetChannelMessages
1188/// [this documentation entry]: https://discord.com/developers/docs/resources/channel#get-channel-messages
1189pub const fn get_channel_messages_limit(limit: u16) -> Result<(), ValidationError> {
1190    if limit >= GET_CHANNEL_MESSAGES_LIMIT_MIN && limit <= GET_CHANNEL_MESSAGES_LIMIT_MAX {
1191        Ok(())
1192    } else {
1193        Err(ValidationError {
1194            kind: ValidationErrorType::GetChannelMessages { limit },
1195        })
1196    }
1197}
1198
1199/// Ensure that the limit for the Get Current User Guilds request is correct.
1200///
1201/// The limit must be at least [`GET_CURRENT_USER_GUILDS_LIMIT_MIN`] and at most
1202/// [`GET_CURRENT_USER_GUILDS_LIMIT_MAX`]. This is based on
1203/// [this documentation entry].
1204///
1205/// # Errors
1206///
1207/// Returns an error of type [`GetCurrentUserGuilds`] if the limit is invalid.
1208///
1209/// [`GetCurrentUserGuilds`]: ValidationErrorType::GetCurrentUserGuilds
1210/// [this documentation entry]: https://discord.com/developers/docs/resources/user#get-current-user-guilds
1211pub const fn get_current_user_guilds_limit(limit: u16) -> Result<(), ValidationError> {
1212    if limit >= GET_CURRENT_USER_GUILDS_LIMIT_MIN && limit <= GET_CURRENT_USER_GUILDS_LIMIT_MAX {
1213        Ok(())
1214    } else {
1215        Err(ValidationError {
1216            kind: ValidationErrorType::GetCurrentUserGuilds { limit },
1217        })
1218    }
1219}
1220
1221/// Ensure that the limit for the Get Entitlements endpoint is correct.
1222///
1223/// The limit must be at least [`GET_ENTITLEMENTS_LIMIT_MIN`] and at most
1224/// [`GET_ENTITLEMENTS_LIMIT_MAX`]. This is based on
1225/// [this documentation entry].
1226///
1227/// # Errors
1228///
1229/// Returns an error of type [`GetEntitlements`] if the limit is invalid.
1230///
1231/// [`GetEntitlements`]: ValidationErrorType::GetEntitlements
1232/// [this documentation entry]: https://discord.com/developers/docs/monetization/entitlements#list-entitlements
1233pub const fn get_entitlements_limit(limit: u8) -> Result<(), ValidationError> {
1234    if limit >= GET_ENTITLEMENTS_LIMIT_MIN && limit <= GET_ENTITLEMENTS_LIMIT_MAX {
1235        Ok(())
1236    } else {
1237        Err(ValidationError {
1238            kind: ValidationErrorType::GetEntitlements { limit },
1239        })
1240    }
1241}
1242
1243/// Ensure that the limit for the Get Guild Audit Log endpoint is correct.
1244///
1245/// The limit must be at least [`GET_GUILD_AUDIT_LOG_LIMIT_MIN`] and at most
1246/// [`GET_GUILD_AUDIT_LOG_LIMIT_MAX`]. This is based on
1247/// [this documentation entry].
1248///
1249/// # Errors
1250///
1251/// Returns an error of type [`GetGuildAuditLog`] if the limit is invalid.
1252///
1253/// [`GetGuildAuditLog`]: ValidationErrorType::GetGuildAuditLog
1254/// [this documentation entry]: https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log
1255pub const fn get_guild_audit_log_limit(limit: u16) -> Result<(), ValidationError> {
1256    if limit >= GET_GUILD_AUDIT_LOG_LIMIT_MIN && limit <= GET_GUILD_AUDIT_LOG_LIMIT_MAX {
1257        Ok(())
1258    } else {
1259        Err(ValidationError {
1260            kind: ValidationErrorType::GetGuildAuditLog { limit },
1261        })
1262    }
1263}
1264
1265/// Ensure that the limit for the Get Guild Bans endpoint is correct.
1266///
1267/// The limit must be at most [`GET_GUILD_BANS_LIMIT_MAX`]. This is based on
1268/// [this documentation entry].
1269///
1270/// # Errors
1271///
1272/// Returns an error of type [`GetGuildBans`] if the limit is invalid.
1273///
1274/// [`GetGuildBans`]: ValidationErrorType::GetGuildBans
1275/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#get-guild-bans
1276pub const fn get_guild_bans_limit(limit: u16) -> Result<(), ValidationError> {
1277    if limit <= GET_GUILD_BANS_LIMIT_MAX {
1278        Ok(())
1279    } else {
1280        Err(ValidationError {
1281            kind: ValidationErrorType::GetGuildBans { limit },
1282        })
1283    }
1284}
1285
1286/// Ensure that the limit for the Get Guild Members endpoint is correct.
1287///
1288/// The limit must be at least [`GET_GUILD_MEMBERS_LIMIT_MIN`] and at most
1289/// [`GET_GUILD_MEMBERS_LIMIT_MAX`]. This is based on
1290/// [this documentation entry].
1291///
1292/// # Errors
1293///
1294/// Returns an error of type [`GetGuildMembers`] if the limit is invalid.
1295///
1296/// [`GetGuildMembers`]: ValidationErrorType::GetGuildMembers
1297/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#list-guild-members
1298pub const fn get_guild_members_limit(limit: u16) -> Result<(), ValidationError> {
1299    if limit >= GET_GUILD_MEMBERS_LIMIT_MIN && limit <= GET_GUILD_MEMBERS_LIMIT_MAX {
1300        Ok(())
1301    } else {
1302        Err(ValidationError {
1303            kind: ValidationErrorType::GetGuildMembers { limit },
1304        })
1305    }
1306}
1307
1308/// Ensure that the limit for the Get Reactions endpoint is correct.
1309///
1310/// The limit must be at least [`GET_REACTIONS_LIMIT_MIN`] and at most
1311/// [`GET_REACTIONS_LIMIT_MAX`]. This is based on [this documentation entry].
1312///
1313/// # Errors
1314///
1315/// Returns an error of type [`GetReactions`] if the limit is invalid.
1316///
1317/// [`GetReactions`]: ValidationErrorType::GetReactions
1318/// [this documentation entry]: https://discord.com/developers/docs/resources/channel#get-reactions
1319pub const fn get_reactions_limit(limit: u16) -> Result<(), ValidationError> {
1320    if limit >= GET_REACTIONS_LIMIT_MIN && limit <= GET_REACTIONS_LIMIT_MAX {
1321        Ok(())
1322    } else {
1323        Err(ValidationError {
1324            kind: ValidationErrorType::GetReactions { limit },
1325        })
1326    }
1327}
1328
1329/// Ensure that a guild name's length is correct.
1330///
1331/// The length must be at least [`GUILD_NAME_LENGTH_MIN`] and at most
1332/// [`GUILD_NAME_LENGTH_MAX`]. This is based on [this documentation entry].
1333///
1334/// # Errors
1335///
1336/// Returns an error of type [`GuildName`] if the length is invalid.
1337///
1338/// [`GuildName`]: ValidationErrorType::GuildName
1339/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#guild-object
1340pub fn guild_name(name: impl AsRef<str>) -> Result<(), ValidationError> {
1341    let len = name.as_ref().chars().count();
1342
1343    if (GUILD_NAME_LENGTH_MIN..=GUILD_NAME_LENGTH_MAX).contains(&len) {
1344        Ok(())
1345    } else {
1346        Err(ValidationError {
1347            kind: ValidationErrorType::GuildName { len },
1348        })
1349    }
1350}
1351
1352/// Ensure that the days to prune members from a guild is correct.
1353///
1354/// The days must be at least [`GUILD_PRUNE_DAYS_MIN`] and at most
1355/// [`GUILD_PRUNE_DAYS_MAX`]. This is based on [this documentation entry].
1356///
1357/// # Errors
1358///
1359/// Returns an error of type [`GuildPruneDays`] if the days is invalid.
1360///
1361/// [`GuildPruneDays`]: ValidationErrorType::GuildPruneDays
1362/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#get-guild-prune-count
1363pub const fn guild_prune_days(days: u16) -> Result<(), ValidationError> {
1364    if days >= GUILD_PRUNE_DAYS_MIN && days <= GUILD_PRUNE_DAYS_MAX {
1365        Ok(())
1366    } else {
1367        Err(ValidationError {
1368            kind: ValidationErrorType::GuildPruneDays { days },
1369        })
1370    }
1371}
1372
1373/// Ensure that the invite max age is correct.
1374///
1375/// The age must be at most [`INVITE_AGE_MAX`]. This is based on
1376/// [this documentation entry].
1377///
1378/// # Errors
1379///
1380/// Returns an error of type [`InviteMaxAge`] if the age is invalid.
1381///
1382/// [`InviteMaxAge`]: ValidationErrorType::InviteMaxAge
1383/// [this documentation entry]: https://discord.com/developers/docs/resources/channel#create-channel-invite
1384pub const fn invite_max_age(max_age: u32) -> Result<(), ValidationError> {
1385    if max_age <= INVITE_AGE_MAX {
1386        Ok(())
1387    } else {
1388        Err(ValidationError {
1389            kind: ValidationErrorType::InviteMaxAge { max_age },
1390        })
1391    }
1392}
1393
1394/// Ensure that the invite max uses is correct.
1395///
1396/// The age must be at most [`INVITE_USES_MAX`]. This is based on
1397/// [this documentation entry].
1398///
1399/// # Errors
1400///
1401/// Returns an error of type [`InviteMaxUses`] if the uses is invalid.
1402///
1403/// [`InviteMaxUses`]: ValidationErrorType::InviteMaxUses
1404/// [this documentation entry]: https://discord.com/developers/docs/resources/channel#create-channel-invite
1405pub const fn invite_max_uses(max_uses: u16) -> Result<(), ValidationError> {
1406    if max_uses <= INVITE_USES_MAX {
1407        Ok(())
1408    } else {
1409        Err(ValidationError {
1410            kind: ValidationErrorType::InviteMaxUses { max_uses },
1411        })
1412    }
1413}
1414
1415/// Ensure that the nickname length is correct.
1416///
1417/// The length must be at least [`NICKNAME_LIMIT_MIN`] and at most
1418/// [`NICKNAME_LIMIT_MAX`]. This is based on [this documentation entry].
1419///
1420/// # Errors
1421///
1422/// Returns an error of type [`Nickname`] if the length is invalid.
1423///
1424/// [`Nickname`]: ValidationErrorType::Nickname
1425/// [this documentation entry]: https://discord.com/developers/docs/resources/user#usernames-and-nicknames
1426pub fn nickname(nickname: impl AsRef<str>) -> Result<(), ValidationError> {
1427    let len = nickname.as_ref().chars().count();
1428
1429    if (NICKNAME_LIMIT_MIN..=NICKNAME_LIMIT_MAX).contains(&len) {
1430        Ok(())
1431    } else {
1432        Err(ValidationError {
1433            kind: ValidationErrorType::Nickname { len },
1434        })
1435    }
1436}
1437
1438/// Ensure that a scheduled event's description is correct.
1439///
1440/// The length must be at least [`SCHEDULED_EVENT_DESCRIPTION_MIN`] and at most
1441/// [`SCHEDULED_EVENT_DESCRIPTION_MAX`]. This is based on
1442/// [this documentation entry].
1443///
1444/// # Errors
1445///
1446/// Returns an error of type [`ScheduledEventDescription`] if the length is
1447/// invalid.
1448///
1449/// [`ScheduledEventDescription`]: ValidationErrorType::ScheduledEventDescription
1450/// [this documentation entry]: https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-structure
1451pub fn scheduled_event_description(description: impl AsRef<str>) -> Result<(), ValidationError> {
1452    let len = description.as_ref().chars().count();
1453
1454    if (SCHEDULED_EVENT_DESCRIPTION_MIN..=SCHEDULED_EVENT_DESCRIPTION_MAX).contains(&len) {
1455        Ok(())
1456    } else {
1457        Err(ValidationError {
1458            kind: ValidationErrorType::ScheduledEventDescription { len },
1459        })
1460    }
1461}
1462
1463/// Ensure that a scheduled event's get users limit amount is correct.
1464///
1465/// The length must be at least [`SCHEDULED_EVENT_GET_USERS_MIN`] and at most
1466/// [`SCHEDULED_EVENT_GET_USERS_MAX`]. This is based on [this documentation
1467/// entry].
1468///
1469/// # Errors
1470///
1471/// Returns an error of type [`ScheduledEventGetUsers`] if the limit is invalid.
1472///
1473/// [`ScheduledEventGetUsers`]: ValidationErrorType::ScheduledEventGetUsers
1474/// [this documentation entry]: https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users-query-string-params
1475pub const fn scheduled_event_get_users(limit: u16) -> Result<(), ValidationError> {
1476    if limit >= SCHEDULED_EVENT_GET_USERS_MIN && limit <= SCHEDULED_EVENT_GET_USERS_MAX {
1477        Ok(())
1478    } else {
1479        Err(ValidationError {
1480            kind: ValidationErrorType::ScheduledEventGetUsers { limit },
1481        })
1482    }
1483}
1484
1485/// Ensure that a scheduled event's name is correct.
1486///
1487/// The length must be at least [`SCHEDULED_EVENT_NAME_MIN`] and at most
1488/// [`SCHEDULED_EVENT_NAME_MAX`]. This is based on [this documentation entry].
1489///
1490/// # Errors
1491///
1492/// Returns an error of type [`ScheduledEventName`] if the length is invalid.
1493///
1494/// [`ScheduledEventName`]: ValidationErrorType::ScheduledEventName
1495/// [this documentation entry]: https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-structure
1496pub fn scheduled_event_name(name: impl AsRef<str>) -> Result<(), ValidationError> {
1497    let len = name.as_ref().chars().count();
1498
1499    if (SCHEDULED_EVENT_NAME_MIN..=SCHEDULED_EVENT_NAME_MAX).contains(&len) {
1500        Ok(())
1501    } else {
1502        Err(ValidationError {
1503            kind: ValidationErrorType::ScheduledEventName { len },
1504        })
1505    }
1506}
1507
1508/// Ensure that the limit for the Search Guild Members endpoint is correct.
1509///
1510/// The limit must be at least [`SEARCH_GUILD_MEMBERS_LIMIT_MIN`] and at most
1511/// [`SEARCH_GUILD_MEMBERS_LIMIT_MAX`]. This is based on
1512/// [this documentation entry].
1513///
1514/// # Errors
1515///
1516/// Returns an error of type [`SearchGuildMembers`] if the limit is invalid.
1517///
1518/// [`SearchGuildMembers`]: ValidationErrorType::SearchGuildMembers
1519/// [this documentation entry]: https://discord.com/developers/docs/resources/guild#search-guild-members
1520pub const fn search_guild_members_limit(limit: u16) -> Result<(), ValidationError> {
1521    if limit >= SEARCH_GUILD_MEMBERS_LIMIT_MIN && limit <= SEARCH_GUILD_MEMBERS_LIMIT_MAX {
1522        Ok(())
1523    } else {
1524        Err(ValidationError {
1525            kind: ValidationErrorType::SearchGuildMembers { limit },
1526        })
1527    }
1528}
1529
1530/// Ensure that the stage instance's topic length is correct.
1531///
1532/// The length must be at least [`STAGE_TOPIC_LENGTH_MIN`] and at most
1533/// [`STAGE_TOPIC_LENGTH_MAX`]. This is based on [this documentation entry].
1534///
1535/// # Errors
1536///
1537/// Returns an error of type [`StageTopic`] if the length is invalid.
1538///
1539/// [`StageTopic`]: ValidationErrorType::StageTopic
1540/// [this documentation entry]: https://discord.com/developers/docs/resources/stage-instance#stage-instance-object
1541pub fn stage_topic(topic: impl AsRef<str>) -> Result<(), ValidationError> {
1542    let len = topic.as_ref().chars().count();
1543
1544    if (STAGE_TOPIC_LENGTH_MIN..=STAGE_TOPIC_LENGTH_MAX).contains(&len) {
1545        Ok(())
1546    } else {
1547        Err(ValidationError {
1548            kind: ValidationErrorType::StageTopic { len },
1549        })
1550    }
1551}
1552
1553/// Ensure that a guild template's description length is correct.
1554///
1555/// The length must be at most [`TEMPLATE_DESCRIPTION_LENGTH_MAX`]. This is
1556/// based on [this documentation entry].
1557///
1558/// # Errors
1559///
1560/// Returns an error of type [`TemplateDescription`] if the length is invalid.
1561///
1562/// [`TemplateDescription`]: ValidationErrorType::TemplateDescription
1563/// [this documentation entry]: https://discord.com/developers/docs/resources/guild-template#guild-template-object-guild-template-structure
1564pub fn template_description(description: impl AsRef<str>) -> Result<(), ValidationError> {
1565    let len = description.as_ref().chars().count();
1566
1567    if len <= TEMPLATE_DESCRIPTION_LENGTH_MAX {
1568        Ok(())
1569    } else {
1570        Err(ValidationError {
1571            kind: ValidationErrorType::TemplateDescription { len },
1572        })
1573    }
1574}
1575
1576/// Ensure that a guild template's name length is correct.
1577///
1578/// The length must be at least [`TEMPLATE_NAME_LENGTH_MIN`] and at most
1579/// [`TEMPLATE_NAME_LENGTH_MAX`]. This is based on [this documentation entry].
1580///
1581/// # Errors
1582///
1583/// Returns an error of type [`TemplateName`] if the length is invalid.
1584///
1585/// [`TemplateName`]: ValidationErrorType::TemplateName
1586/// [this documentation entry]: https://discord.com/developers/docs/resources/guild-template#guild-template-object-guild-template-structure
1587pub fn template_name(name: impl AsRef<str>) -> Result<(), ValidationError> {
1588    let len = name.as_ref().chars().count();
1589
1590    if (TEMPLATE_NAME_LENGTH_MIN..=TEMPLATE_NAME_LENGTH_MAX).contains(&len) {
1591        Ok(())
1592    } else {
1593        Err(ValidationError {
1594            kind: ValidationErrorType::TemplateName { len },
1595        })
1596    }
1597}
1598
1599/// Ensure that a username is correct.
1600///
1601/// The length must be at least [`USERNAME_LIMIT_MIN`] and at most
1602/// [`USERNAME_LIMIT_MAX`]. It must also be free of certain substrings. This is
1603/// based on [this documentation entry].
1604///
1605/// # Errors
1606///
1607/// Returns an error of type [`Username`] if the length is invalid.
1608///
1609/// [`Username`]: ValidationErrorType::Username
1610/// [this documentation entry]: https://discord.com/developers/docs/resources/user#usernames-and-nicknames
1611pub fn username(value: impl AsRef<str>) -> Result<(), ValidationError> {
1612    let value = value.as_ref();
1613    let len = value.chars().count();
1614
1615    let range = USERNAME_LIMIT_MIN..=USERNAME_LIMIT_MAX;
1616    let invalid_len = (!range.contains(&len)).then_some(len);
1617
1618    let invalid_substring = USERNAME_INVALID_SUBSTRINGS
1619        .into_iter()
1620        .find(|invalid_substring| value.contains(invalid_substring))
1621        .or_else(|| {
1622            USERNAME_INVALID_STRINGS
1623                .into_iter()
1624                .find(|invalid_string| value == *invalid_string)
1625        });
1626
1627    if invalid_len.is_none() && invalid_substring.is_none() {
1628        Ok(())
1629    } else {
1630        Err(ValidationError {
1631            kind: ValidationErrorType::Username {
1632                len: invalid_len,
1633                substring: invalid_substring,
1634            },
1635        })
1636    }
1637}
1638
1639/// Ensure that a webhook is correct.
1640///
1641/// The length must be at least [`WEBHOOK_USERNAME_LIMIT_MIN`] and at most
1642/// [`WEBHOOK_USERNAME_LIMIT_MAX`]. It must also be free of certain substrings.
1643/// This is based on [this documentation entry].
1644///
1645/// # Errors
1646///
1647/// Returns an error of type [`WebhookUsername`] if the length is invalid.
1648///
1649/// [`WebhookUsername`]: ValidationErrorType::WebhookUsername
1650/// [this documentation entry]: https://discord.com/developers/docs/resources/webhook#create-webhook
1651pub fn webhook_username(value: impl AsRef<str>) -> Result<(), ValidationError> {
1652    let value = value.as_ref();
1653    let len = value.chars().count();
1654
1655    let range = WEBHOOK_USERNAME_LIMIT_MIN..=WEBHOOK_USERNAME_LIMIT_MAX;
1656    let invalid_len = (!range.contains(&len)).then_some(len);
1657
1658    let invalid_substring = WEBHOOK_INVALID_STRINGS
1659        .into_iter()
1660        .find(|invalid_string| value == *invalid_string);
1661
1662    if invalid_len.is_none() && invalid_substring.is_none() {
1663        Ok(())
1664    } else {
1665        Err(ValidationError {
1666            kind: ValidationErrorType::WebhookUsername {
1667                len: invalid_len,
1668                substring: invalid_substring,
1669            },
1670        })
1671    }
1672}
1673
1674#[cfg(test)]
1675mod tests {
1676    use super::*;
1677
1678    #[test]
1679    fn username_variants() {
1680        let expected = format!(
1681            "provided username length is 200, but it must be at least {USERNAME_LIMIT_MIN} and at \
1682            most {USERNAME_LIMIT_MAX}, and cannot contain :"
1683        );
1684        let actual = ValidationError {
1685            kind: ValidationErrorType::Username {
1686                len: Some(200),
1687                substring: Some(":"),
1688            },
1689        };
1690        assert_eq!(expected, actual.to_string());
1691
1692        let expected = format!(
1693            "provided username length is 200, but it must be at least {USERNAME_LIMIT_MIN} and at \
1694            most {USERNAME_LIMIT_MAX}",
1695        );
1696        let actual = ValidationError {
1697            kind: ValidationErrorType::Username {
1698                len: Some(200),
1699                substring: None,
1700            },
1701        };
1702        assert_eq!(expected, actual.to_string());
1703
1704        let expected = "provided username cannot contain :".to_string();
1705        let actual = ValidationError {
1706            kind: ValidationErrorType::Username {
1707                len: None,
1708                substring: Some(":"),
1709            },
1710        };
1711        assert_eq!(expected, actual.to_string());
1712    }
1713
1714    #[test]
1715    fn audit_reason_length() {
1716        assert!(audit_reason("").is_ok());
1717        assert!(audit_reason("a").is_ok());
1718        assert!(audit_reason("a".repeat(500)).is_ok());
1719        assert!(audit_reason("a".repeat(512)).is_ok());
1720
1721        assert!(audit_reason("a".repeat(513)).is_err());
1722    }
1723
1724    #[test]
1725    fn auto_moderation_block_action_custom_message() {
1726        assert!(auto_moderation_block_action_custom_message_limit("").is_ok());
1727        assert!(auto_moderation_block_action_custom_message_limit("a".repeat(150)).is_ok());
1728        assert!(matches!(
1729            auto_moderation_block_action_custom_message_limit("a".repeat(151))
1730                .unwrap_err()
1731                .kind,
1732            ValidationErrorType::AutoModerationBlockActionCustomMessageLimit { len: 151 }
1733        ));
1734    }
1735
1736    #[test]
1737    fn auto_moderation_metadata_mention_total() {
1738        assert!(auto_moderation_metadata_mention_total_limit(0).is_ok());
1739        assert!(auto_moderation_metadata_mention_total_limit(1).is_ok());
1740        assert!(auto_moderation_metadata_mention_total_limit(50).is_ok());
1741
1742        assert!(auto_moderation_metadata_mention_total_limit(51).is_err());
1743    }
1744
1745    #[test]
1746    fn auto_moderation_metadata_keyword_filter_max() {
1747        let mut keywords = (0..1000).map(|_| "a").collect::<Vec<_>>();
1748
1749        assert!(auto_moderation_metadata_keyword_filter(&[] as &[&str]).is_ok());
1750        assert!(auto_moderation_metadata_keyword_filter(&["a".repeat(60)]).is_ok());
1751        assert!(auto_moderation_metadata_keyword_filter(&keywords).is_ok());
1752
1753        keywords.push("a");
1754
1755        assert!(auto_moderation_metadata_keyword_filter(&["a".repeat(61)]).is_err());
1756        assert!(auto_moderation_metadata_keyword_filter(&keywords).is_err());
1757    }
1758
1759    #[test]
1760    fn auto_moderation_metadata_keyword_allow_list_max() {
1761        let mut allow_list = (0..100).map(|_| "a").collect::<Vec<_>>();
1762
1763        assert!(auto_moderation_metadata_keyword_allow_list(&[] as &[&str]).is_ok());
1764        assert!(auto_moderation_metadata_keyword_allow_list(&["a".repeat(60)]).is_ok());
1765
1766        allow_list.push("a");
1767
1768        assert!(auto_moderation_metadata_keyword_allow_list(&["a".repeat(61)]).is_err());
1769        assert!(auto_moderation_metadata_keyword_allow_list(&allow_list).is_err());
1770    }
1771
1772    #[test]
1773    fn auto_moderation_metadata_preset_allow_list_max() {
1774        let mut allow_list = (0..1000).map(|_| "a").collect::<Vec<_>>();
1775
1776        assert!(auto_moderation_metadata_preset_allow_list(&[] as &[&str]).is_ok());
1777        assert!(auto_moderation_metadata_preset_allow_list(&["a".repeat(60)]).is_ok());
1778
1779        allow_list.push("a");
1780
1781        assert!(auto_moderation_metadata_preset_allow_list(&["a".repeat(61)]).is_err());
1782        assert!(auto_moderation_metadata_preset_allow_list(&allow_list).is_err());
1783    }
1784
1785    #[test]
1786    fn auto_moderation_metadata_regex_patterns_max() {
1787        let mut patterns = (0..10).map(|_| "a").collect::<Vec<_>>();
1788
1789        assert!(auto_moderation_metadata_regex_patterns(&[] as &[&str]).is_ok());
1790        assert!(auto_moderation_metadata_regex_patterns(&["a".repeat(260)]).is_ok());
1791
1792        patterns.push("a");
1793
1794        assert!(auto_moderation_metadata_regex_patterns(&["a".repeat(261)]).is_err());
1795        assert!(auto_moderation_metadata_regex_patterns(&patterns).is_err());
1796    }
1797
1798    #[test]
1799    fn auto_moderation_exempt_roles_max() {
1800        let mut roles = (1..=20).map(Id::new).collect::<Vec<_>>();
1801
1802        assert!(auto_moderation_exempt_roles(&[]).is_ok());
1803        assert!(auto_moderation_exempt_roles(&roles).is_ok());
1804
1805        roles.push(Id::new(21));
1806
1807        assert!(auto_moderation_exempt_roles(&roles).is_err());
1808    }
1809
1810    #[test]
1811    fn auto_moderation_exempt_channels_max() {
1812        let mut channels = (1..=50).map(Id::new).collect::<Vec<_>>();
1813
1814        assert!(auto_moderation_exempt_channels(&[]).is_ok());
1815        assert!(auto_moderation_exempt_channels(&channels).is_ok());
1816
1817        channels.push(Id::new(51));
1818
1819        assert!(auto_moderation_exempt_channels(&channels).is_err());
1820    }
1821
1822    #[test]
1823    fn auto_moderation_action_metadata_duration_seconds_max() {
1824        assert!(auto_moderation_action_metadata_duration_seconds(0).is_ok());
1825        assert!(auto_moderation_action_metadata_duration_seconds(1).is_ok());
1826        assert!(auto_moderation_action_metadata_duration_seconds(2_419_200).is_ok());
1827
1828        assert!(auto_moderation_action_metadata_duration_seconds(2_419_201).is_err());
1829    }
1830
1831    #[test]
1832    fn create_guild_ban_delete_message_seconds_max() {
1833        assert!(create_guild_ban_delete_message_seconds(0).is_ok());
1834        assert!(create_guild_ban_delete_message_seconds(1).is_ok());
1835        assert!(create_guild_ban_delete_message_seconds(604_800).is_ok());
1836
1837        assert!(create_guild_ban_delete_message_seconds(604_801).is_err());
1838    }
1839
1840    #[test]
1841    fn communication_disabled_until_max() {
1842        #[allow(clippy::cast_possible_wrap)]
1843        let now = SystemTime::now()
1844            .duration_since(UNIX_EPOCH)
1845            .unwrap()
1846            .as_secs() as i64;
1847
1848        let ok_timestamp =
1849            Timestamp::from_secs(now + COMMUNICATION_DISABLED_MAX_DURATION - 1000).unwrap();
1850        assert!(communication_disabled_until(ok_timestamp).is_ok());
1851
1852        let err_timestamp =
1853            Timestamp::from_secs(now + COMMUNICATION_DISABLED_MAX_DURATION + 1000).unwrap();
1854        assert!(communication_disabled_until(err_timestamp).is_err());
1855    }
1856
1857    #[test]
1858    fn get_channel_messages_limit_count() {
1859        assert!(get_channel_messages_limit(1).is_ok());
1860        assert!(get_channel_messages_limit(100).is_ok());
1861
1862        assert!(get_channel_messages_limit(0).is_err());
1863        assert!(get_channel_messages_limit(101).is_err());
1864    }
1865
1866    #[test]
1867    fn get_current_user_guilds_limit_count() {
1868        assert!(get_current_user_guilds_limit(1).is_ok());
1869        assert!(get_current_user_guilds_limit(200).is_ok());
1870
1871        assert!(get_current_user_guilds_limit(0).is_err());
1872        assert!(get_current_user_guilds_limit(201).is_err());
1873    }
1874
1875    #[test]
1876    fn get_guild_log_limit_count() {
1877        assert!(get_guild_audit_log_limit(1).is_ok());
1878        assert!(get_guild_audit_log_limit(100).is_ok());
1879
1880        assert!(get_guild_audit_log_limit(0).is_err());
1881        assert!(get_guild_audit_log_limit(101).is_err());
1882    }
1883
1884    #[test]
1885    fn get_guild_bans_limit_count() {
1886        assert!(get_guild_bans_limit(0).is_ok());
1887        assert!(get_guild_bans_limit(1000).is_ok());
1888
1889        assert!(get_guild_bans_limit(1001).is_err());
1890    }
1891
1892    #[test]
1893    fn get_guild_members_limit_count() {
1894        assert!(get_guild_members_limit(1).is_ok());
1895        assert!(get_guild_members_limit(1000).is_ok());
1896
1897        assert!(get_guild_members_limit(0).is_err());
1898        assert!(get_guild_members_limit(1001).is_err());
1899    }
1900
1901    #[test]
1902    fn get_reactions_limit_count() {
1903        assert!(get_reactions_limit(1).is_ok());
1904        assert!(get_reactions_limit(100).is_ok());
1905
1906        assert!(get_reactions_limit(0).is_err());
1907        assert!(get_reactions_limit(101).is_err());
1908    }
1909
1910    #[test]
1911    fn guild_name_length() {
1912        assert!(guild_name("aa").is_ok());
1913        assert!(guild_name("a".repeat(100)).is_ok());
1914
1915        assert!(guild_name("").is_err());
1916        assert!(guild_name("a").is_err());
1917        assert!(guild_name("a".repeat(101)).is_err());
1918    }
1919
1920    #[test]
1921    fn guild_prune_days_length() {
1922        assert!(guild_prune_days(1).is_ok());
1923        assert!(guild_prune_days(30).is_ok());
1924
1925        assert!(guild_prune_days(0).is_err());
1926        assert!(guild_prune_days(31).is_err());
1927        assert!(guild_prune_days(100).is_err());
1928    }
1929
1930    #[test]
1931    fn invite_max_age_length() {
1932        assert!(invite_max_age(0).is_ok());
1933        assert!(invite_max_age(86_400).is_ok());
1934        assert!(invite_max_age(604_800).is_ok());
1935
1936        assert!(invite_max_age(604_801).is_err());
1937    }
1938
1939    #[test]
1940    fn invite_max_uses_count() {
1941        assert!(invite_max_uses(0).is_ok());
1942        assert!(invite_max_uses(100).is_ok());
1943
1944        assert!(invite_max_uses(101).is_err());
1945    }
1946
1947    #[test]
1948    fn nickname_length() {
1949        assert!(nickname("a").is_ok());
1950        assert!(nickname("a".repeat(32)).is_ok());
1951
1952        assert!(nickname("").is_err());
1953        assert!(nickname("a".repeat(33)).is_err());
1954    }
1955
1956    #[test]
1957    fn scheduled_event_description_length() {
1958        assert!(scheduled_event_description("a").is_ok());
1959        assert!(scheduled_event_description("a".repeat(1000)).is_ok());
1960
1961        assert!(scheduled_event_description("").is_err());
1962        assert!(scheduled_event_description("a".repeat(1001)).is_err());
1963    }
1964
1965    #[test]
1966    fn scheduled_event_get_users_length() {
1967        assert!(scheduled_event_get_users(0).is_err());
1968        assert!(scheduled_event_get_users(101).is_err());
1969        assert!(scheduled_event_get_users(100).is_ok());
1970        assert!(scheduled_event_get_users(1).is_ok());
1971    }
1972
1973    #[test]
1974    fn scheduled_event_name_length() {
1975        assert!(scheduled_event_name("a").is_ok());
1976        assert!(scheduled_event_name("a".repeat(100)).is_ok());
1977
1978        assert!(scheduled_event_name("").is_err());
1979        assert!(scheduled_event_name("a".repeat(101)).is_err());
1980    }
1981
1982    #[test]
1983    fn search_guild_members_limit_count() {
1984        assert!(search_guild_members_limit(1).is_ok());
1985        assert!(search_guild_members_limit(1000).is_ok());
1986
1987        assert!(search_guild_members_limit(0).is_err());
1988        assert!(search_guild_members_limit(1001).is_err());
1989    }
1990
1991    #[test]
1992    fn stage_topic_length() {
1993        assert!(stage_topic("a").is_ok());
1994        assert!(stage_topic("a".repeat(120)).is_ok());
1995
1996        assert!(stage_topic("").is_err());
1997        assert!(stage_topic("a".repeat(121)).is_err());
1998    }
1999
2000    #[test]
2001    fn template_description_length() {
2002        assert!(template_description("").is_ok());
2003        assert!(template_description("a").is_ok());
2004        assert!(template_description("a".repeat(120)).is_ok());
2005
2006        assert!(template_description("a".repeat(121)).is_err());
2007    }
2008
2009    #[test]
2010    fn template_name_length() {
2011        assert!(template_name("a").is_ok());
2012        assert!(template_name("a".repeat(100)).is_ok());
2013
2014        assert!(template_name("").is_err());
2015        assert!(template_name("a".repeat(101)).is_err());
2016    }
2017
2018    #[test]
2019    fn username_length() {
2020        assert!(username("aa").is_ok());
2021        assert!(username("a".repeat(32)).is_ok());
2022
2023        assert!(username("a").is_err());
2024        assert!(username("a".repeat(33)).is_err());
2025
2026        assert!(username("no @ in username").is_err());
2027        assert!(username("no # in username").is_err());
2028        assert!(username("no : in username").is_err());
2029        assert!(username(r"no ``` in username").is_err());
2030        assert!(username("no discord in username").is_err());
2031        assert!(username("everyone").is_err());
2032        assert!(username("here").is_err());
2033    }
2034
2035    #[test]
2036    fn webhook_username_length() {
2037        assert!(webhook_username("aa").is_ok());
2038        assert!(webhook_username("a".repeat(80)).is_ok());
2039
2040        assert!(webhook_username("a").is_err());
2041        assert!(webhook_username("a".repeat(81)).is_err());
2042
2043        assert!(webhook_username("clyde").is_err());
2044    }
2045}