twilight_http/request/guild/create_guild/
mod.rs1use crate::{
2 client::Client,
3 error::Error as HttpError,
4 request::{Request, TryIntoRequest},
5 response::{Response, ResponseFuture},
6 routing::Route,
7};
8use serde::Serialize;
9use std::future::IntoFuture;
10use std::{
11 error::Error,
12 fmt::{Display, Formatter, Result as FmtResult},
13};
14use twilight_model::{
15 channel::ChannelType,
16 guild::{
17 AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, PartialGuild,
18 Permissions, SystemChannelFlags, VerificationLevel,
19 },
20 http::permission_overwrite::PermissionOverwrite,
21 id::{
22 marker::{ChannelMarker, RoleMarker},
23 Id,
24 },
25};
26use twilight_validate::request::guild_name as validate_guild_name;
27
28mod builder;
29
30pub use self::builder::*;
31
32#[derive(Debug)]
34pub struct CreateGuildError {
35 kind: CreateGuildErrorType,
36 source: Option<Box<dyn Error + Send + Sync>>,
37}
38
39impl CreateGuildError {
40 #[must_use = "retrieving the type has no effect if left unused"]
42 pub const fn kind(&self) -> &CreateGuildErrorType {
43 &self.kind
44 }
45
46 #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
48 pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
49 self.source
50 }
51
52 #[must_use = "consuming the error into its parts has no effect if left unused"]
54 pub fn into_parts(self) -> (CreateGuildErrorType, Option<Box<dyn Error + Send + Sync>>) {
55 (self.kind, self.source)
56 }
57}
58
59impl Display for CreateGuildError {
60 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
61 match &self.kind {
62 CreateGuildErrorType::NameInvalid { .. } => f.write_str("the guild name is invalid"),
63 CreateGuildErrorType::TooManyChannels { .. } => {
64 f.write_str("too many channels were provided")
65 }
66 CreateGuildErrorType::TooManyRoles { .. } => {
67 f.write_str("too many roles were provided")
68 }
69 }
70 }
71}
72
73impl Error for CreateGuildError {}
74
75#[derive(Debug)]
77#[non_exhaustive]
78pub enum CreateGuildErrorType {
79 NameInvalid {
82 name: String,
84 },
85 TooManyChannels {
89 channels: Vec<GuildChannelFields>,
91 },
92 TooManyRoles {
96 roles: Vec<RoleFields>,
98 },
99}
100
101#[derive(Serialize)]
102struct CreateGuildFields<'a> {
103 #[serde(skip_serializing_if = "Option::is_none")]
104 afk_channel_id: Option<Id<ChannelMarker>>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 afk_timeout: Option<AfkTimeout>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 channels: Option<Vec<GuildChannelFields>>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 default_message_notifications: Option<DefaultMessageNotificationLevel>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 explicit_content_filter: Option<ExplicitContentFilter>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 icon: Option<&'a str>,
115 name: String,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 roles: Option<Vec<RoleFields>>,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 system_channel_id: Option<Id<ChannelMarker>>,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 system_channel_flags: Option<SystemChannelFlags>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 verification_level: Option<VerificationLevel>,
124}
125
126#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
130pub struct RoleFields {
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub color: Option<u32>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub hoist: Option<bool>,
135 pub id: Id<RoleMarker>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub mentionable: Option<bool>,
138 pub name: String,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub permissions: Option<Permissions>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub position: Option<i64>,
143}
144
145#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
149#[non_exhaustive]
150#[serde(untagged)]
151pub enum GuildChannelFields {
152 Category(CategoryFields),
153 Text(TextFields),
154 Voice(VoiceFields),
155}
156
157impl GuildChannelFields {
158 pub const fn id(&self) -> Id<ChannelMarker> {
159 match self {
160 Self::Category(c) => c.id,
161 Self::Text(t) => t.id,
162 Self::Voice(v) => v.id,
163 }
164 }
165}
166
167#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
171pub struct CategoryFields {
172 pub id: Id<ChannelMarker>,
173 #[serde(rename = "type")]
174 pub kind: ChannelType,
175 pub name: String,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
178}
179
180#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
184pub struct TextFields {
185 pub id: Id<ChannelMarker>,
186 #[serde(rename = "type")]
187 pub kind: ChannelType,
188 pub name: String,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub nsfw: Option<bool>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub parent_id: Option<Id<ChannelMarker>>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub rate_limit_per_user: Option<u16>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub topic: Option<String>,
199}
200
201#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
205pub struct VoiceFields {
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub bitrate: Option<u32>,
208 pub id: Id<ChannelMarker>,
209 #[serde(rename = "type")]
210 pub kind: ChannelType,
211 pub name: String,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub parent_id: Option<Id<ChannelMarker>>,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub user_limit: Option<u16>,
218}
219
220#[must_use = "requests must be configured and executed"]
225pub struct CreateGuild<'a> {
226 fields: Result<CreateGuildFields<'a>, CreateGuildError>,
227 http: &'a Client,
228}
229
230impl<'a> CreateGuild<'a> {
231 pub(crate) fn new(http: &'a Client, name: String) -> Self {
232 let fields = Ok(CreateGuildFields {
233 afk_channel_id: None,
234 afk_timeout: None,
235 channels: None,
236 default_message_notifications: None,
237 explicit_content_filter: None,
238 icon: None,
239 name: String::new(),
240 roles: None,
241 system_channel_id: None,
242 system_channel_flags: None,
243 verification_level: None,
244 })
245 .and_then(|mut fields| {
246 validate_guild_name(&name).map_err(|source| CreateGuildError {
247 kind: CreateGuildErrorType::NameInvalid { name: name.clone() },
248 source: Some(Box::new(source)),
249 })?;
250
251 fields.name = name;
252
253 Ok(fields)
254 });
255
256 Self { fields, http }
257 }
258
259 #[allow(clippy::missing_panics_doc)]
261 pub fn add_role(mut self, role: RoleFields) -> Self {
262 if let Ok(fields) = self.fields.as_mut() {
263 if fields.roles.is_none() {
264 let builder = RoleFieldsBuilder::new("@everyone".to_owned());
265 fields.roles.replace(vec![builder.build().unwrap()]);
266 }
267
268 if let Some(roles) = fields.roles.as_mut() {
269 roles.push(role);
270 }
271 }
272
273 self
274 }
275
276 pub fn afk_channel_id(mut self, afk_channel_id: Id<ChannelMarker>) -> Self {
282 if let Ok(fields) = self.fields.as_mut() {
283 fields.afk_channel_id = Some(afk_channel_id);
284 }
285
286 self
287 }
288
289 pub fn afk_timeout(mut self, afk_timeout: AfkTimeout) -> Self {
291 if let Ok(fields) = self.fields.as_mut() {
292 fields.afk_timeout = Some(afk_timeout);
293 }
294
295 self
296 }
297
298 pub fn channels(mut self, channels: Vec<GuildChannelFields>) -> Self {
341 self.fields = self.fields.and_then(|mut fields| {
342 if channels.len() > 500 {
345 return Err(CreateGuildError {
346 kind: CreateGuildErrorType::TooManyChannels { channels },
347 source: None,
348 });
349 }
350
351 fields.channels.replace(channels);
352
353 Ok(fields)
354 });
355
356 self
357 }
358
359 pub fn default_message_notifications(
364 mut self,
365 default_message_notifications: DefaultMessageNotificationLevel,
366 ) -> Self {
367 if let Ok(fields) = self.fields.as_mut() {
368 fields.default_message_notifications = Some(default_message_notifications);
369 }
370
371 self
372 }
373
374 pub fn explicit_content_filter(
376 mut self,
377 explicit_content_filter: ExplicitContentFilter,
378 ) -> Self {
379 if let Ok(fields) = self.fields.as_mut() {
380 fields.explicit_content_filter = Some(explicit_content_filter);
381 }
382
383 self
384 }
385
386 pub fn icon(mut self, icon: &'a str) -> Self {
394 if let Ok(fields) = self.fields.as_mut() {
395 fields.icon.replace(icon);
396 }
397
398 self
399 }
400
401 pub fn override_everyone(mut self, everyone: RoleFields) -> Self {
409 if let Ok(fields) = self.fields.as_mut() {
410 if let Some(roles) = fields.roles.as_mut() {
411 roles.remove(0);
412 roles.insert(0, everyone);
413 } else {
414 fields.roles.replace(vec![everyone]);
415 }
416 }
417
418 self
419 }
420
421 pub fn system_channel_id(mut self, system_channel_id: Id<ChannelMarker>) -> Self {
427 if let Ok(fields) = self.fields.as_mut() {
428 fields.system_channel_id = Some(system_channel_id);
429 }
430
431 self
432 }
433
434 pub fn system_channel_flags(mut self, system_channel_flags: SystemChannelFlags) -> Self {
436 if let Ok(fields) = self.fields.as_mut() {
437 fields.system_channel_flags = Some(system_channel_flags);
438 }
439
440 self
441 }
442
443 #[allow(clippy::missing_panics_doc)]
469 pub fn roles(mut self, mut roles: Vec<RoleFields>) -> Self {
470 self.fields = self.fields.and_then(|mut fields| {
471 if roles.len() > 250 {
472 return Err(CreateGuildError {
473 kind: CreateGuildErrorType::TooManyRoles { roles },
474 source: None,
475 });
476 }
477
478 if let Some(prev_roles) = fields.roles.as_mut() {
479 roles.insert(0, prev_roles.remove(0));
480 } else {
481 let builder = RoleFieldsBuilder::new("@everyone".to_owned());
482 roles.insert(0, builder.build().unwrap());
483 }
484
485 fields.roles.replace(roles);
486
487 Ok(fields)
488 });
489
490 self
491 }
492}
493
494impl IntoFuture for CreateGuild<'_> {
495 type Output = Result<Response<PartialGuild>, HttpError>;
496
497 type IntoFuture = ResponseFuture<PartialGuild>;
498
499 fn into_future(self) -> Self::IntoFuture {
500 let http = self.http;
501
502 match self.try_into_request() {
503 Ok(request) => http.request(request),
504 Err(source) => ResponseFuture::error(source),
505 }
506 }
507}
508
509impl TryIntoRequest for CreateGuild<'_> {
510 fn try_into_request(self) -> Result<Request, HttpError> {
511 let fields = self.fields.map_err(HttpError::validation)?;
512
513 Request::builder(&Route::CreateGuild).json(&fields).build()
514 }
515}