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        forum::{DefaultReaction, ForumLayout, ForumSortOrder, ForumTag},
13        permission_overwrite::PermissionOverwrite,
14        thread::AutoArchiveDuration,
15        Channel, ChannelType, VideoQualityMode,
16    },
17    id::{
18        marker::{ChannelMarker, GuildMarker},
19        Id,
20    },
21};
22use twilight_validate::{
23    channel::{
24        bitrate as validate_bitrate, name as validate_name,
25        rate_limit_per_user as validate_rate_limit_per_user, topic as validate_topic,
26        ChannelValidationError,
27    },
28    request::{audit_reason as validate_audit_reason, ValidationError},
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 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 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 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 fn default_reaction_emoji(mut self, default_reaction_emoji: &'a DefaultReaction) -> Self {
179        if let Ok(fields) = self.fields.as_mut() {
180            fields.default_reaction_emoji = Some(default_reaction_emoji);
181        }
182
183        self
184    }
185
186    /// Set the default sort order for newly created forum channels.
187    pub fn default_sort_order(mut self, default_sort_order: ForumSortOrder) -> Self {
188        if let Ok(fields) = self.fields.as_mut() {
189            fields.default_sort_order = Some(default_sort_order);
190        }
191
192        self
193    }
194
195    /// Set the default number of seconds that a user must wait before before they are
196    /// able to send another message in any newly-created thread in the channel.
197    ///
198    /// This field is only applicable for text, announcement, media, and forum channels.
199    /// The minimum is 0 and the maximum is 21600. This is also known as "Slow Mode". See
200    /// [Discord Docs/Channel Object].
201    ///
202    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
203    pub fn default_thread_rate_limit_per_user(
204        mut self,
205        default_thread_rate_limit_per_user: u16,
206    ) -> Self {
207        self.fields = self.fields.and_then(|mut fields| {
208            validate_rate_limit_per_user(default_thread_rate_limit_per_user)?;
209
210            fields.default_thread_rate_limit_per_user = Some(default_thread_rate_limit_per_user);
211
212            Ok(fields)
213        });
214
215        self
216    }
217
218    /// Set the kind of channel.
219    pub fn kind(mut self, kind: ChannelType) -> Self {
220        if let Ok(fields) = self.fields.as_mut() {
221            fields.kind = Some(kind);
222        }
223
224        self
225    }
226
227    /// Set whether the channel is marked as NSFW.
228    pub fn nsfw(mut self, nsfw: bool) -> Self {
229        if let Ok(fields) = self.fields.as_mut() {
230            fields.nsfw = Some(nsfw);
231        }
232
233        self
234    }
235
236    /// If this is specified, and the parent ID is a `ChannelType::CategoryChannel`, create this
237    /// channel as a child of the category channel.
238    pub fn parent_id(mut self, parent_id: Id<ChannelMarker>) -> Self {
239        if let Ok(fields) = self.fields.as_mut() {
240            fields.parent_id = Some(parent_id);
241        }
242
243        self
244    }
245
246    /// Set the permission overwrites of a channel.
247    pub fn permission_overwrites(
248        mut self,
249        permission_overwrites: &'a [PermissionOverwrite],
250    ) -> Self {
251        if let Ok(fields) = self.fields.as_mut() {
252            fields.permission_overwrites = Some(permission_overwrites);
253        }
254
255        self
256    }
257
258    /// Set the position of the channel.
259    ///
260    /// Positions are numerical and zero-indexed. If you place a channel at position 2, channels
261    /// 2-n will shift down one position and the initial channel will take its place.
262    pub fn position(mut self, position: u64) -> Self {
263        if let Ok(fields) = self.fields.as_mut() {
264            fields.position = Some(position);
265        }
266
267        self
268    }
269
270    /// Set the number of seconds that a user must wait before before they are able to send another
271    /// message.
272    ///
273    /// The minimum is 0 and the maximum is 21600. This is also known as "Slow
274    /// Mode". See [Discord Docs/Channel Object].
275    ///
276    /// # Errors
277    ///
278    /// Returns an error of type [`RateLimitPerUserInvalid`] if the name is
279    /// invalid.
280    ///
281    /// [`RateLimitPerUserInvalid`]: twilight_validate::channel::ChannelValidationErrorType::RateLimitPerUserInvalid
282    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
283    pub fn rate_limit_per_user(mut self, rate_limit_per_user: u16) -> Self {
284        self.fields = self.fields.and_then(|mut fields| {
285            validate_rate_limit_per_user(rate_limit_per_user)?;
286
287            fields.rate_limit_per_user = Some(rate_limit_per_user);
288
289            Ok(fields)
290        });
291
292        self
293    }
294
295    /// For voice and stage channels, set the channel's RTC region.
296    pub fn rtc_region(mut self, rtc_region: &'a str) -> Self {
297        if let Ok(fields) = self.fields.as_mut() {
298            fields.rtc_region = Some(rtc_region);
299        }
300
301        self
302    }
303
304    /// Set the topic.
305    ///
306    /// The maximum length is 1024 UTF-16 characters. See
307    /// [Discord Docs/Channel Object].
308    ///
309    /// # Errors
310    ///
311    /// Returns an error of type [`TopicInvalid`] if the name is
312    /// invalid.
313    ///
314    /// [`TopicInvalid`]: twilight_validate::channel::ChannelValidationErrorType::TopicInvalid
315    /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure
316    pub fn topic(mut self, topic: &'a str) -> Self {
317        self.fields = self.fields.and_then(|mut fields| {
318            validate_topic(topic)?;
319
320            fields.topic.replace(topic);
321
322            Ok(fields)
323        });
324
325        self
326    }
327
328    /// For voice channels, set the user limit.
329    ///
330    /// Set to 0 for no limit. Limit can otherwise be between 1 and 99
331    /// inclusive. See [Discord Docs/Modify Channel] for more details.
332    ///
333    /// [Discord Docs/Modify Channel]: https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel
334    pub fn user_limit(mut self, user_limit: u16) -> Self {
335        if let Ok(fields) = self.fields.as_mut() {
336            fields.user_limit = Some(user_limit);
337        }
338
339        self
340    }
341
342    /// For voice channels, set the channel's video quality mode.
343    pub fn video_quality_mode(mut self, video_quality_mode: VideoQualityMode) -> Self {
344        if let Ok(fields) = self.fields.as_mut() {
345            fields.video_quality_mode = Some(video_quality_mode);
346        }
347
348        self
349    }
350}
351
352impl<'a> AuditLogReason<'a> for CreateGuildChannel<'a> {
353    fn reason(mut self, reason: &'a str) -> Self {
354        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
355
356        self
357    }
358}
359
360impl IntoFuture for CreateGuildChannel<'_> {
361    type Output = Result<Response<Channel>, Error>;
362
363    type IntoFuture = ResponseFuture<Channel>;
364
365    fn into_future(self) -> Self::IntoFuture {
366        let http = self.http;
367
368        match self.try_into_request() {
369            Ok(request) => http.request(request),
370            Err(source) => ResponseFuture::error(source),
371        }
372    }
373}
374
375impl TryIntoRequest for CreateGuildChannel<'_> {
376    fn try_into_request(self) -> Result<Request, Error> {
377        let fields = self.fields.map_err(Error::validation)?;
378        let mut request = Request::builder(&Route::CreateChannel {
379            guild_id: self.guild_id.get(),
380        })
381        .json(&fields);
382
383        if let Some(reason) = self.reason.map_err(Error::validation)? {
384            request = request.headers(request::audit_header(reason)?);
385        }
386
387        request.build()
388    }
389}