use crate::request::prelude::*;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::{permission_overwrite::PermissionOverwrite, ChannelType},
guild::{
DefaultMessageNotificationLevel, ExplicitContentFilter, PartialGuild, Permissions,
VerificationLevel,
},
id::{ChannelId, RoleId},
};
mod builder;
pub use self::builder::*;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum CreateGuildError {
NameInvalid {
name: String,
},
TooManyChannels {
channels: Vec<GuildChannelFields>,
},
TooManyRoles {
roles: Vec<RoleFields>,
},
}
impl Display for CreateGuildError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::NameInvalid { .. } => f.write_str("the guild name is invalid"),
Self::TooManyChannels { .. } => f.write_str("too many channels were provided"),
Self::TooManyRoles { .. } => f.write_str("too many roles were provided"),
}
}
}
impl Error for CreateGuildError {}
#[derive(Serialize)]
struct CreateGuildFields {
#[serde(skip_serializing_if = "Option::is_none")]
channels: Option<Vec<GuildChannelFields>>,
#[serde(skip_serializing_if = "Option::is_none")]
default_message_notifications: Option<DefaultMessageNotificationLevel>,
#[serde(skip_serializing_if = "Option::is_none")]
explicit_content_filter: Option<ExplicitContentFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<String>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
region: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
roles: Option<Vec<RoleFields>>,
#[serde(skip_serializing_if = "Option::is_none")]
verification_level: Option<VerificationLevel>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct RoleFields {
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hoist: Option<bool>,
pub id: RoleId,
#[serde(skip_serializing_if = "Option::is_none")]
pub mentionable: Option<bool>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<Permissions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<i64>,
}
impl From<RoleFieldsBuilder> for RoleFields {
fn from(builder: RoleFieldsBuilder) -> Self {
builder.build()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(untagged)]
pub enum GuildChannelFields {
Category(CategoryFields),
Text(TextFields),
Voice(VoiceFields),
}
impl GuildChannelFields {
pub fn id(self) -> ChannelId {
match self {
Self::Category(c) => c.id,
Self::Text(t) => t.id,
Self::Voice(v) => v.id,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct CategoryFields {
pub id: ChannelId,
#[serde(rename = "type")]
pub kind: ChannelType,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct TextFields {
pub id: ChannelId,
#[serde(rename = "type")]
pub kind: ChannelType,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub nsfw: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<ChannelId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_per_user: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic: Option<String>,
}
impl From<TextFieldsBuilder> for TextFields {
fn from(builder: TextFieldsBuilder) -> TextFields {
builder.build()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct VoiceFields {
#[serde(skip_serializing_if = "Option::is_none")]
pub bitrate: Option<u64>,
pub id: ChannelId,
#[serde(rename = "type")]
pub kind: ChannelType,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<ChannelId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_limit: Option<u64>,
}
impl From<VoiceFieldsBuilder> for VoiceFields {
fn from(builder: VoiceFieldsBuilder) -> VoiceFields {
builder.build()
}
}
pub struct CreateGuild<'a> {
fields: CreateGuildFields,
fut: Option<Pending<'a, PartialGuild>>,
http: &'a Client,
}
impl<'a> CreateGuild<'a> {
pub(crate) fn new(http: &'a Client, name: impl Into<String>) -> Result<Self, CreateGuildError> {
Self::_new(http, name.into())
}
fn _new(http: &'a Client, name: String) -> Result<Self, CreateGuildError> {
if !validate::guild_name(&name) {
return Err(CreateGuildError::NameInvalid { name });
}
Ok(Self {
fields: CreateGuildFields {
channels: None,
default_message_notifications: None,
explicit_content_filter: None,
icon: None,
name,
region: None,
roles: None,
verification_level: None,
},
fut: None,
http,
})
}
pub fn add_role(mut self, role: impl Into<RoleFields>) -> Self {
if self.fields.roles.is_none() {
let builder = RoleFieldsBuilder::new("@everyone");
self.fields.roles.replace(vec![builder.build()]);
}
if let Some(roles) = self.fields.roles.as_mut() {
roles.push(role.into());
}
self
}
pub fn channels(mut self, channels: Vec<GuildChannelFields>) -> Result<Self, CreateGuildError> {
if channels.len() > 500 {
return Err(CreateGuildError::TooManyChannels { channels });
}
self.fields.channels.replace(channels);
Ok(self)
}
pub fn default_message_notifications(
mut self,
default_message_notifications: DefaultMessageNotificationLevel,
) -> Self {
self.fields
.default_message_notifications
.replace(default_message_notifications);
self
}
pub fn explicit_content_filter(
mut self,
explicit_content_filter: ExplicitContentFilter,
) -> Self {
self.fields
.explicit_content_filter
.replace(explicit_content_filter);
self
}
pub fn icon(mut self, icon: impl Into<String>) -> Self {
self.fields.icon.replace(icon.into());
self
}
pub fn override_everyone(mut self, everyone: impl Into<RoleFields>) -> Self {
if let Some(roles) = self.fields.roles.as_mut() {
roles.remove(0);
roles.insert(0, everyone.into());
} else {
self.fields.roles.replace(vec![everyone.into()]);
}
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.fields.region.replace(region.into());
self
}
pub fn roles(mut self, mut roles: Vec<RoleFields>) -> Result<Self, CreateGuildError> {
if roles.len() > 250 {
return Err(CreateGuildError::TooManyRoles { roles });
}
if let Some(prev_roles) = self.fields.roles.as_mut() {
roles.insert(0, prev_roles.remove(0));
} else {
let builder = RoleFieldsBuilder::new("@everyone");
roles.insert(0, builder.build());
}
self.fields.roles.replace(roles);
Ok(self)
}
fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from((
crate::json_to_vec(&self.fields)?,
Route::CreateGuild,
)))));
Ok(())
}
}
poll_req!(CreateGuild<'_>, PartialGuild);