twilight_http/request/guild/
create_guild_channel.rs

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