Skip to main content

twilight_http/request/guild/
create_guild_channel.rs

1#[cfg(not(target_os = "wasi"))]
2use crate::response::{Response, ResponseFuture};
3use crate::{
4    client::Client,
5    error::Error,
6    request::{self, AuditLogReason, Request, TryIntoRequest},
7    routing::Route,
8};
9use serde::Serialize;
10use std::future::IntoFuture;
11use twilight_model::{
12    channel::{
13        Channel, ChannelType, VideoQualityMode,
14        forum::{DefaultReaction, ForumLayout, ForumSortOrder, ForumTag},
15        permission_overwrite::PermissionOverwrite,
16        thread::AutoArchiveDuration,
17    },
18    id::{
19        Id,
20        marker::{ChannelMarker, GuildMarker},
21    },
22};
23use twilight_validate::{
24    channel::{
25        ChannelValidationError, bitrate as validate_bitrate, name as validate_name,
26        rate_limit_per_user as validate_rate_limit_per_user, topic as validate_topic,
27    },
28    request::{ValidationError, audit_reason as validate_audit_reason},
29};
30
31#[derive(Serialize)]
32struct CreateGuildChannelFields<'a> {
33    #[serde(skip_serializing_if = "Option::is_none")]
34    available_tags: Option<&'a [ForumTag]>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    bitrate: Option<u32>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    default_auto_archive_duration: Option<AutoArchiveDuration>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    default_forum_layout: Option<ForumLayout>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    default_reaction_emoji: Option<&'a DefaultReaction>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    default_sort_order: Option<ForumSortOrder>,
45    /// Initial `rate_limit_per_user` to set on newly created threads in a channel.
46    /// This field is copied to the thread at creation time and does not live update.
47    ///
48    /// This field is only applicable for text, announcement, media, and forum channels.
49    #[serde(skip_serializing_if = "Option::is_none")]
50    default_thread_rate_limit_per_user: Option<u16>,
51    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
52    kind: Option<ChannelType>,
53    name: &'a str,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    nsfw: Option<bool>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    parent_id: Option<Id<ChannelMarker>>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    permission_overwrites: Option<&'a [PermissionOverwrite]>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    position: Option<u64>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    rate_limit_per_user: Option<u16>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    rtc_region: Option<&'a str>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    topic: Option<&'a str>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    user_limit: Option<u16>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    video_quality_mode: Option<VideoQualityMode>,
72}
73
74/// Create a new request to create a guild channel.
75///
76/// All fields are optional except for name. The minimum length of the name is 1
77/// UTF-16 characters and the maximum is 100 UTF-16 characters.
78#[must_use = "requests must be configured and executed"]
79pub struct CreateGuildChannel<'a> {
80    fields: Result<CreateGuildChannelFields<'a>, ChannelValidationError>,
81    guild_id: Id<GuildMarker>,
82    http: &'a Client,
83    reason: Result<Option<&'a str>, ValidationError>,
84}
85
86impl<'a> CreateGuildChannel<'a> {
87    pub(crate) fn new(http: &'a Client, guild_id: Id<GuildMarker>, name: &'a str) -> Self {
88        let fields = Ok(CreateGuildChannelFields {
89            available_tags: None,
90            bitrate: None,
91            default_auto_archive_duration: None,
92            default_forum_layout: None,
93            default_reaction_emoji: None,
94            default_sort_order: None,
95            default_thread_rate_limit_per_user: None,
96            kind: None,
97            name,
98            nsfw: None,
99            parent_id: None,
100            permission_overwrites: None,
101            position: None,
102            rate_limit_per_user: None,
103            rtc_region: None,
104            topic: None,
105            user_limit: None,
106            video_quality_mode: None,
107        })
108        .and_then(|fields| {
109            validate_name(name)?;
110
111            Ok(fields)
112        });
113
114        Self {
115            fields,
116            guild_id,
117            http,
118            reason: Ok(None),
119        }
120    }
121
122    /// Set the available tags for the forum.
123    pub const fn available_tags(mut self, available_tags: &'a [ForumTag]) -> Self {
124        if let Ok(fields) = self.fields.as_mut() {
125            fields.available_tags = Some(available_tags);
126        }
127
128        self
129    }
130
131    /// For voice and stage channels, set the bitrate of the channel.
132    ///
133    /// Must be at least 8000.
134    ///
135    /// # Errors
136    ///
137    /// Returns an error of type [`BitrateInvalid`] if the bitrate is invalid.
138    ///
139    /// [`BitrateInvalid`]: twilight_validate::channel::ChannelValidationErrorType::BitrateInvalid
140    pub fn bitrate(mut self, bitrate: u32) -> Self {
141        self.fields = self.fields.and_then(|mut fields| {
142            validate_bitrate(bitrate)?;
143
144            fields.bitrate = Some(bitrate);
145
146            Ok(fields)
147        });
148
149        self
150    }
151
152    /// Set the default auto archive duration for newly created threads in the
153    /// channel.
154    ///
155    /// Automatic archive durations are not locked behind the guild's boost
156    /// level.
157    pub const fn default_auto_archive_duration(
158        mut self,
159        auto_archive_duration: AutoArchiveDuration,
160    ) -> Self {
161        if let Ok(fields) = self.fields.as_mut() {
162            fields.default_auto_archive_duration = Some(auto_archive_duration);
163        }
164
165        self
166    }
167
168    /// Set the default forum layout for new forum channels.
169    pub const fn default_forum_layout(mut self, default_forum_layout: ForumLayout) -> Self {
170        if let Ok(fields) = self.fields.as_mut() {
171            fields.default_forum_layout = Some(default_forum_layout);
172        }
173
174        self
175    }
176
177    /// Set the default reaction emoji for new forum threads.
178    pub const fn default_reaction_emoji(
179        mut self,
180        default_reaction_emoji: &'a DefaultReaction,
181    ) -> Self {
182        if let Ok(fields) = self.fields.as_mut() {
183            fields.default_reaction_emoji = Some(default_reaction_emoji);
184        }
185
186        self
187    }
188
189    /// Set the default sort order for newly created forum channels.
190    pub const fn default_sort_order(mut self, default_sort_order: ForumSortOrder) -> Self {
191        if let Ok(fields) = self.fields.as_mut() {
192            fields.default_sort_order = Some(default_sort_order);
193        }
194
195        self
196    }
197
198    /// Set the default number of seconds that a user must wait before before they are
199    /// able to send another message in any newly-created thread in the channel.
200    ///
201    /// This field is only applicable for text, announcement, media, and forum channels.
202    /// The minimum is 0 and the maximum is 21600. This is also known as "Slow Mode". See
203    /// [Discord Docs/Channel Object].
204    ///
205    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
206    pub fn default_thread_rate_limit_per_user(
207        mut self,
208        default_thread_rate_limit_per_user: u16,
209    ) -> Self {
210        self.fields = self.fields.and_then(|mut fields| {
211            validate_rate_limit_per_user(default_thread_rate_limit_per_user)?;
212
213            fields.default_thread_rate_limit_per_user = Some(default_thread_rate_limit_per_user);
214
215            Ok(fields)
216        });
217
218        self
219    }
220
221    /// Set the kind of channel.
222    pub const fn kind(mut self, kind: ChannelType) -> Self {
223        if let Ok(fields) = self.fields.as_mut() {
224            fields.kind = Some(kind);
225        }
226
227        self
228    }
229
230    /// Set whether the channel is marked as NSFW.
231    pub const fn nsfw(mut self, nsfw: bool) -> Self {
232        if let Ok(fields) = self.fields.as_mut() {
233            fields.nsfw = Some(nsfw);
234        }
235
236        self
237    }
238
239    /// If this is specified, and the parent ID is a `ChannelType::CategoryChannel`, create this
240    /// channel as a child of the category channel.
241    pub const fn parent_id(mut self, parent_id: Id<ChannelMarker>) -> Self {
242        if let Ok(fields) = self.fields.as_mut() {
243            fields.parent_id = Some(parent_id);
244        }
245
246        self
247    }
248
249    /// Set the permission overwrites of a channel.
250    pub const fn permission_overwrites(
251        mut self,
252        permission_overwrites: &'a [PermissionOverwrite],
253    ) -> Self {
254        if let Ok(fields) = self.fields.as_mut() {
255            fields.permission_overwrites = Some(permission_overwrites);
256        }
257
258        self
259    }
260
261    /// Set the position of the channel.
262    ///
263    /// Positions are numerical and zero-indexed. If you place a channel at position 2, channels
264    /// 2-n will shift down one position and the initial channel will take its place.
265    pub const fn position(mut self, position: u64) -> Self {
266        if let Ok(fields) = self.fields.as_mut() {
267            fields.position = Some(position);
268        }
269
270        self
271    }
272
273    /// Set the number of seconds that a user must wait before before they are able to send another
274    /// message.
275    ///
276    /// The minimum is 0 and the maximum is 21600. This is also known as "Slow
277    /// Mode". See [Discord Docs/Channel Object].
278    ///
279    /// # Errors
280    ///
281    /// Returns an error of type [`RateLimitPerUserInvalid`] if the name is
282    /// invalid.
283    ///
284    /// [`RateLimitPerUserInvalid`]: twilight_validate::channel::ChannelValidationErrorType::RateLimitPerUserInvalid
285    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
286    pub fn rate_limit_per_user(mut self, rate_limit_per_user: u16) -> Self {
287        self.fields = self.fields.and_then(|mut fields| {
288            validate_rate_limit_per_user(rate_limit_per_user)?;
289
290            fields.rate_limit_per_user = Some(rate_limit_per_user);
291
292            Ok(fields)
293        });
294
295        self
296    }
297
298    /// For voice and stage channels, set the channel's RTC region.
299    pub const fn rtc_region(mut self, rtc_region: &'a str) -> Self {
300        if let Ok(fields) = self.fields.as_mut() {
301            fields.rtc_region = Some(rtc_region);
302        }
303
304        self
305    }
306
307    /// Set the topic.
308    ///
309    /// The maximum length is 1024 UTF-16 characters. See
310    /// [Discord Docs/Channel Object].
311    ///
312    /// # Errors
313    ///
314    /// Returns an error of type [`TopicInvalid`] if the name is
315    /// invalid.
316    ///
317    /// [`TopicInvalid`]: twilight_validate::channel::ChannelValidationErrorType::TopicInvalid
318    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
319    pub fn topic(mut self, topic: &'a str) -> Self {
320        self.fields = self.fields.and_then(|mut fields| {
321            validate_topic(topic)?;
322
323            fields.topic.replace(topic);
324
325            Ok(fields)
326        });
327
328        self
329    }
330
331    /// For voice channels, set the user limit.
332    ///
333    /// Set to 0 for no limit. Limit can otherwise be between 1 and 99
334    /// inclusive. See [Discord Docs/Modify Channel] for more details.
335    ///
336    /// [Discord Docs/Modify Channel]: https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel
337    pub const fn user_limit(mut self, user_limit: u16) -> Self {
338        if let Ok(fields) = self.fields.as_mut() {
339            fields.user_limit = Some(user_limit);
340        }
341
342        self
343    }
344
345    /// For voice channels, set the channel's video quality mode.
346    pub const fn video_quality_mode(mut self, video_quality_mode: VideoQualityMode) -> Self {
347        if let Ok(fields) = self.fields.as_mut() {
348            fields.video_quality_mode = Some(video_quality_mode);
349        }
350
351        self
352    }
353}
354
355impl<'a> AuditLogReason<'a> for CreateGuildChannel<'a> {
356    fn reason(mut self, reason: &'a str) -> Self {
357        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
358
359        self
360    }
361}
362
363#[cfg(not(target_os = "wasi"))]
364impl IntoFuture for CreateGuildChannel<'_> {
365    type Output = Result<Response<Channel>, Error>;
366
367    type IntoFuture = ResponseFuture<Channel>;
368
369    fn into_future(self) -> Self::IntoFuture {
370        let http = self.http;
371
372        match self.try_into_request() {
373            Ok(request) => http.request(request),
374            Err(source) => ResponseFuture::error(source),
375        }
376    }
377}
378
379impl TryIntoRequest for CreateGuildChannel<'_> {
380    fn try_into_request(self) -> Result<Request, Error> {
381        let fields = self.fields.map_err(Error::validation)?;
382        let mut request = Request::builder(&Route::CreateChannel {
383            guild_id: self.guild_id.get(),
384        })
385        .json(&fields);
386
387        if let Some(reason) = self.reason.map_err(Error::validation)? {
388            request = request.headers(request::audit_header(reason)?);
389        }
390
391        request.build()
392    }
393}