twilight_http/request/guild/create_guild/
mod.rsuse crate::{
client::Client,
error::Error as HttpError,
request::{Request, TryIntoRequest},
response::{Response, ResponseFuture},
routing::Route,
};
use serde::Serialize;
use std::future::IntoFuture;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::ChannelType,
guild::{
AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, PartialGuild,
Permissions, SystemChannelFlags, VerificationLevel,
},
http::permission_overwrite::PermissionOverwrite,
id::{
marker::{ChannelMarker, RoleMarker},
Id,
},
};
use twilight_validate::request::guild_name as validate_guild_name;
mod builder;
pub use self::builder::*;
#[derive(Debug)]
pub struct CreateGuildError {
kind: CreateGuildErrorType,
source: Option<Box<dyn Error + Send + Sync>>,
}
impl CreateGuildError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &CreateGuildErrorType {
&self.kind
}
#[must_use = "consuming the error and retrieving the source has no effect if left unused"]
pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
self.source
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(self) -> (CreateGuildErrorType, Option<Box<dyn Error + Send + Sync>>) {
(self.kind, self.source)
}
}
impl Display for CreateGuildError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
CreateGuildErrorType::NameInvalid { .. } => f.write_str("the guild name is invalid"),
CreateGuildErrorType::TooManyChannels { .. } => {
f.write_str("too many channels were provided")
}
CreateGuildErrorType::TooManyRoles { .. } => {
f.write_str("too many roles were provided")
}
}
}
}
impl Error for CreateGuildError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum CreateGuildErrorType {
NameInvalid {
name: String,
},
TooManyChannels {
channels: Vec<GuildChannelFields>,
},
TooManyRoles {
roles: Vec<RoleFields>,
},
}
#[derive(Serialize)]
struct CreateGuildFields<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
afk_channel_id: Option<Id<ChannelMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
afk_timeout: Option<AfkTimeout>,
#[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<&'a str>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
roles: Option<Vec<RoleFields>>,
#[serde(skip_serializing_if = "Option::is_none")]
system_channel_id: Option<Id<ChannelMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
system_channel_flags: Option<SystemChannelFlags>,
#[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: Id<RoleMarker>,
#[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>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(untagged)]
pub enum GuildChannelFields {
Category(CategoryFields),
Text(TextFields),
Voice(VoiceFields),
}
impl GuildChannelFields {
pub const fn id(&self) -> Id<ChannelMarker> {
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: Id<ChannelMarker>,
#[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: Id<ChannelMarker>,
#[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<Id<ChannelMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_per_user: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct VoiceFields {
#[serde(skip_serializing_if = "Option::is_none")]
pub bitrate: Option<u32>,
pub id: Id<ChannelMarker>,
#[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<Id<ChannelMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_limit: Option<u16>,
}
#[must_use = "requests must be configured and executed"]
pub struct CreateGuild<'a> {
fields: Result<CreateGuildFields<'a>, CreateGuildError>,
http: &'a Client,
}
impl<'a> CreateGuild<'a> {
pub(crate) fn new(http: &'a Client, name: String) -> Self {
let fields = Ok(CreateGuildFields {
afk_channel_id: None,
afk_timeout: None,
channels: None,
default_message_notifications: None,
explicit_content_filter: None,
icon: None,
name: String::new(),
roles: None,
system_channel_id: None,
system_channel_flags: None,
verification_level: None,
})
.and_then(|mut fields| {
validate_guild_name(&name).map_err(|source| CreateGuildError {
kind: CreateGuildErrorType::NameInvalid { name: name.clone() },
source: Some(Box::new(source)),
})?;
fields.name = name;
Ok(fields)
});
Self { fields, http }
}
#[allow(clippy::missing_panics_doc)]
pub fn add_role(mut self, role: RoleFields) -> Self {
if let Ok(fields) = self.fields.as_mut() {
if fields.roles.is_none() {
let builder = RoleFieldsBuilder::new("@everyone".to_owned());
fields.roles.replace(vec![builder.build().unwrap()]);
}
if let Some(roles) = fields.roles.as_mut() {
roles.push(role);
}
}
self
}
pub fn afk_channel_id(mut self, afk_channel_id: Id<ChannelMarker>) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.afk_channel_id = Some(afk_channel_id);
}
self
}
pub fn afk_timeout(mut self, afk_timeout: AfkTimeout) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.afk_timeout = Some(afk_timeout);
}
self
}
pub fn channels(mut self, channels: Vec<GuildChannelFields>) -> Self {
self.fields = self.fields.and_then(|mut fields| {
if channels.len() > 500 {
return Err(CreateGuildError {
kind: CreateGuildErrorType::TooManyChannels { channels },
source: None,
});
}
fields.channels.replace(channels);
Ok(fields)
});
self
}
pub fn default_message_notifications(
mut self,
default_message_notifications: DefaultMessageNotificationLevel,
) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.default_message_notifications = Some(default_message_notifications);
}
self
}
pub fn explicit_content_filter(
mut self,
explicit_content_filter: ExplicitContentFilter,
) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.explicit_content_filter = Some(explicit_content_filter);
}
self
}
pub fn icon(mut self, icon: &'a str) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.icon.replace(icon);
}
self
}
pub fn override_everyone(mut self, everyone: RoleFields) -> Self {
if let Ok(fields) = self.fields.as_mut() {
if let Some(roles) = fields.roles.as_mut() {
roles.remove(0);
roles.insert(0, everyone);
} else {
fields.roles.replace(vec![everyone]);
}
}
self
}
pub fn system_channel_id(mut self, system_channel_id: Id<ChannelMarker>) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.system_channel_id = Some(system_channel_id);
}
self
}
pub fn system_channel_flags(mut self, system_channel_flags: SystemChannelFlags) -> Self {
if let Ok(fields) = self.fields.as_mut() {
fields.system_channel_flags = Some(system_channel_flags);
}
self
}
#[allow(clippy::missing_panics_doc)]
pub fn roles(mut self, mut roles: Vec<RoleFields>) -> Self {
self.fields = self.fields.and_then(|mut fields| {
if roles.len() > 250 {
return Err(CreateGuildError {
kind: CreateGuildErrorType::TooManyRoles { roles },
source: None,
});
}
if let Some(prev_roles) = fields.roles.as_mut() {
roles.insert(0, prev_roles.remove(0));
} else {
let builder = RoleFieldsBuilder::new("@everyone".to_owned());
roles.insert(0, builder.build().unwrap());
}
fields.roles.replace(roles);
Ok(fields)
});
self
}
}
impl IntoFuture for CreateGuild<'_> {
type Output = Result<Response<PartialGuild>, HttpError>;
type IntoFuture = ResponseFuture<PartialGuild>;
fn into_future(self) -> Self::IntoFuture {
let http = self.http;
match self.try_into_request() {
Ok(request) => http.request(request),
Err(source) => ResponseFuture::error(source),
}
}
}
impl TryIntoRequest for CreateGuild<'_> {
fn try_into_request(self) -> Result<Request, HttpError> {
let fields = self.fields.map_err(HttpError::validation)?;
Request::builder(&Route::CreateGuild).json(&fields).build()
}
}