twilight_validate/
channel.rs1use std::{
4 error::Error,
5 fmt::{Display, Formatter, Result as FmtResult},
6};
7use twilight_model::channel::ChannelType;
8
9pub const CHANNEL_BITRATE_MIN: u32 = 8000;
11
12pub const CHANNEL_BULK_DELETE_MESSAGES_MAX: usize = 100;
14
15pub const CHANNEL_BULK_DELETE_MESSAGES_MIN: usize = 2;
17
18pub const CHANNEL_FORUM_TOPIC_LENGTH_MAX: usize = 4096;
20
21pub const CHANNEL_NAME_LENGTH_MAX: usize = 100;
23
24pub const CHANNEL_NAME_LENGTH_MIN: usize = 1;
26
27pub const CHANNEL_RATE_LIMIT_PER_USER_MAX: u16 = 21_600;
29
30pub const CHANNEL_THREAD_GET_MEMBERS_LIMIT_MAX: u32 = 100;
32
33pub const CHANNEL_THREAD_GET_MEMBERS_LIMIT_MIN: u32 = 1;
35
36pub const CHANNEL_TOPIC_LENGTH_MAX: usize = 1024;
38
39pub const CHANNEL_USER_LIMIT_MAX: u16 = 99;
41
42#[derive(Debug)]
44pub struct ChannelValidationError {
45 kind: ChannelValidationErrorType,
47}
48
49impl ChannelValidationError {
50 #[must_use = "retrieving the type has no effect if left unused"]
52 pub const fn kind(&self) -> &ChannelValidationErrorType {
53 &self.kind
54 }
55
56 #[allow(clippy::unused_self)]
58 #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
59 pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
60 None
61 }
62
63 #[must_use = "consuming the error into its parts has no effect if left unused"]
65 pub fn into_parts(
66 self,
67 ) -> (
68 ChannelValidationErrorType,
69 Option<Box<dyn Error + Send + Sync>>,
70 ) {
71 (self.kind, None)
72 }
73}
74
75impl Display for ChannelValidationError {
76 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
77 match &self.kind {
78 ChannelValidationErrorType::BitrateInvalid => {
79 f.write_str("bitrate is less than ")?;
80 Display::fmt(&CHANNEL_BITRATE_MIN, f)
81 }
82 ChannelValidationErrorType::BulkDeleteMessagesInvalid => {
83 f.write_str("number of messages deleted in bulk is less than ")?;
84 Display::fmt(&CHANNEL_BULK_DELETE_MESSAGES_MIN, f)?;
85 f.write_str(" or greater than ")?;
86
87 Display::fmt(&CHANNEL_BULK_DELETE_MESSAGES_MAX, f)
88 }
89 ChannelValidationErrorType::ForumTopicInvalid => {
90 f.write_str("the forum topic is invalid")
91 }
92 ChannelValidationErrorType::NameInvalid => {
93 f.write_str("the length of the name is invalid")
94 }
95 ChannelValidationErrorType::RateLimitPerUserInvalid { .. } => {
96 f.write_str("the rate limit per user is invalid")
97 }
98 ChannelValidationErrorType::ThreadMemberLimitInvalid => {
99 f.write_str("number of members to return is less than ")?;
100 Display::fmt(&CHANNEL_THREAD_GET_MEMBERS_LIMIT_MIN, f)?;
101 f.write_str(" or greater than ")?;
102
103 Display::fmt(&CHANNEL_THREAD_GET_MEMBERS_LIMIT_MAX, f)
104 }
105 ChannelValidationErrorType::TopicInvalid => f.write_str("the topic is invalid"),
106 ChannelValidationErrorType::TypeInvalid { kind } => {
107 Display::fmt(kind.name(), f)?;
108
109 f.write_str(" is not a thread")
110 }
111 ChannelValidationErrorType::UserLimitInvalid => {
112 f.write_str("user limit is greater than ")?;
113
114 Display::fmt(&CHANNEL_USER_LIMIT_MAX, f)
115 }
116 }
117 }
118}
119
120impl Error for ChannelValidationError {}
121
122#[derive(Debug)]
124#[non_exhaustive]
125pub enum ChannelValidationErrorType {
126 BitrateInvalid,
128 BulkDeleteMessagesInvalid,
130 ForumTopicInvalid,
132 NameInvalid,
135 RateLimitPerUserInvalid {
137 rate_limit_per_user: u16,
139 },
140 ThreadMemberLimitInvalid,
142 TopicInvalid,
144 TypeInvalid {
146 kind: ChannelType,
148 },
149 UserLimitInvalid,
151}
152
153pub const fn bitrate(value: u32) -> Result<(), ChannelValidationError> {
163 if value >= CHANNEL_BITRATE_MIN {
164 Ok(())
165 } else {
166 Err(ChannelValidationError {
167 kind: ChannelValidationErrorType::BitrateInvalid,
168 })
169 }
170}
171
172pub const fn bulk_delete_messages(message_count: usize) -> Result<(), ChannelValidationError> {
181 if message_count >= CHANNEL_BULK_DELETE_MESSAGES_MIN
182 && message_count <= CHANNEL_BULK_DELETE_MESSAGES_MAX
183 {
184 Ok(())
185 } else {
186 Err(ChannelValidationError {
187 kind: ChannelValidationErrorType::BulkDeleteMessagesInvalid,
188 })
189 }
190}
191
192pub const fn is_thread(kind: ChannelType) -> Result<(), ChannelValidationError> {
199 if matches!(
200 kind,
201 ChannelType::AnnouncementThread | ChannelType::PublicThread | ChannelType::PrivateThread
202 ) {
203 Ok(())
204 } else {
205 Err(ChannelValidationError {
206 kind: ChannelValidationErrorType::TypeInvalid { kind },
207 })
208 }
209}
210
211pub fn forum_topic(value: impl AsRef<str>) -> Result<(), ChannelValidationError> {
220 let count = value.as_ref().chars().count();
221
222 if count <= CHANNEL_FORUM_TOPIC_LENGTH_MAX {
223 Ok(())
224 } else {
225 Err(ChannelValidationError {
226 kind: ChannelValidationErrorType::TopicInvalid,
227 })
228 }
229}
230
231pub fn name(value: impl AsRef<str>) -> Result<(), ChannelValidationError> {
244 let len = value.as_ref().chars().count();
245
246 if (CHANNEL_NAME_LENGTH_MIN..=CHANNEL_NAME_LENGTH_MAX).contains(&len) {
247 Ok(())
248 } else {
249 Err(ChannelValidationError {
250 kind: ChannelValidationErrorType::NameInvalid,
251 })
252 }
253}
254
255pub const fn rate_limit_per_user(value: u16) -> Result<(), ChannelValidationError> {
268 if value <= CHANNEL_RATE_LIMIT_PER_USER_MAX {
269 Ok(())
270 } else {
271 Err(ChannelValidationError {
272 kind: ChannelValidationErrorType::RateLimitPerUserInvalid {
273 rate_limit_per_user: value,
274 },
275 })
276 }
277}
278
279pub const fn thread_member_limit(value: u32) -> Result<(), ChannelValidationError> {
291 if value >= CHANNEL_THREAD_GET_MEMBERS_LIMIT_MIN
292 && value <= CHANNEL_THREAD_GET_MEMBERS_LIMIT_MAX
293 {
294 Ok(())
295 } else {
296 Err(ChannelValidationError {
297 kind: ChannelValidationErrorType::ThreadMemberLimitInvalid,
298 })
299 }
300}
301
302pub fn topic(value: impl AsRef<str>) -> Result<(), ChannelValidationError> {
311 let count = value.as_ref().chars().count();
312
313 if count <= CHANNEL_TOPIC_LENGTH_MAX {
314 Ok(())
315 } else {
316 Err(ChannelValidationError {
317 kind: ChannelValidationErrorType::TopicInvalid,
318 })
319 }
320}
321
322pub const fn user_limit(value: u16) -> Result<(), ChannelValidationError> {
332 if value <= CHANNEL_USER_LIMIT_MAX {
333 Ok(())
334 } else {
335 Err(ChannelValidationError {
336 kind: ChannelValidationErrorType::UserLimitInvalid,
337 })
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn bulk_delete_messages() {
347 assert!(matches!(
348 super::bulk_delete_messages(0).unwrap_err().kind(),
349 ChannelValidationErrorType::BulkDeleteMessagesInvalid,
350 ));
351 assert!(matches!(
352 super::bulk_delete_messages(1).unwrap_err().kind(),
353 ChannelValidationErrorType::BulkDeleteMessagesInvalid,
354 ));
355 assert!(super::bulk_delete_messages(100).is_ok());
356 assert!(matches!(
357 super::bulk_delete_messages(101).unwrap_err().kind(),
358 ChannelValidationErrorType::BulkDeleteMessagesInvalid,
359 ));
360 }
361
362 #[test]
363 fn channel_bitrate() {
364 assert!(bitrate(8000).is_ok());
365
366 assert!(bitrate(7000).is_err());
367 }
368
369 #[test]
370 fn thread_is_thread() {
371 assert!(is_thread(ChannelType::AnnouncementThread).is_ok());
372 assert!(is_thread(ChannelType::PrivateThread).is_ok());
373 assert!(is_thread(ChannelType::PublicThread).is_ok());
374
375 assert!(is_thread(ChannelType::Group).is_err());
376 }
377
378 #[test]
379 fn channel_name() {
380 assert!(name("a").is_ok());
381 assert!(name("a".repeat(100)).is_ok());
382
383 assert!(name("").is_err());
384 assert!(name("a".repeat(101)).is_err());
385 }
386
387 #[test]
388 fn rate_limit_per_user_value() {
389 assert!(rate_limit_per_user(0).is_ok());
390 assert!(rate_limit_per_user(21_600).is_ok());
391
392 assert!(rate_limit_per_user(21_601).is_err());
393 }
394
395 #[test]
396 fn thread_member_limit_value() {
397 assert!(thread_member_limit(1).is_ok());
398 assert!(thread_member_limit(100).is_ok());
399 assert!(thread_member_limit(50).is_ok());
400
401 assert!(thread_member_limit(0).is_err());
402 assert!(thread_member_limit(101).is_err());
403 }
404
405 #[test]
406 fn topic_length() {
407 assert!(topic("").is_ok());
408 assert!(topic("a").is_ok());
409 assert!(topic("a".repeat(1_024)).is_ok());
410
411 assert!(topic("a".repeat(1_025)).is_err());
412 }
413
414 #[test]
415 fn user_limit() {
416 assert!(super::user_limit(0).is_ok());
417 assert!(super::user_limit(99).is_ok());
418 assert!(matches!(
419 super::user_limit(100).unwrap_err().kind(),
420 ChannelValidationErrorType::UserLimitInvalid
421 ));
422 }
423}