use crate::{
client::Client,
error::Error as HttpError,
request::{self, validate, AuditLogReason, AuditLogReasonError, Pending, Request},
routing::Route,
};
use serde::Serialize;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::{permission_overwrite::PermissionOverwrite, ChannelType, GuildChannel},
id::{ChannelId, GuildId},
};
#[derive(Debug)]
pub struct CreateGuildChannelError {
kind: CreateGuildChannelErrorType,
}
impl CreateGuildChannelError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &CreateGuildChannelErrorType {
&self.kind
}
#[allow(clippy::unused_self)]
#[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>> {
None
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(
self,
) -> (
CreateGuildChannelErrorType,
Option<Box<dyn Error + Send + Sync>>,
) {
(self.kind, None)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum CreateGuildChannelErrorType {
NameInvalid {
name: String,
},
RateLimitPerUserInvalid {
rate_limit_per_user: u64,
},
TopicInvalid {
topic: String,
},
}
impl Display for CreateGuildChannelError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
CreateGuildChannelErrorType::NameInvalid { .. } => {
f.write_str("the length of the name is invalid")
}
CreateGuildChannelErrorType::RateLimitPerUserInvalid { .. } => {
f.write_str("the rate limit per user is invalid")
}
CreateGuildChannelErrorType::TopicInvalid { .. } => f.write_str("the topic is invalid"),
}
}
}
impl Error for CreateGuildChannelError {}
#[derive(Serialize)]
struct CreateGuildChannelFields {
#[serde(skip_serializing_if = "Option::is_none")]
bitrate: Option<u64>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
kind: Option<ChannelType>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
nsfw: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
parent_id: Option<ChannelId>,
#[serde(skip_serializing_if = "Option::is_none")]
permission_overwrites: Option<Vec<PermissionOverwrite>>,
#[serde(skip_serializing_if = "Option::is_none")]
position: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
rate_limit_per_user: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
topic: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
user_limit: Option<u64>,
}
pub struct CreateGuildChannel<'a> {
fields: CreateGuildChannelFields,
fut: Option<Pending<'a, GuildChannel>>,
guild_id: GuildId,
http: &'a Client,
reason: Option<String>,
}
impl<'a> CreateGuildChannel<'a> {
pub(crate) fn new(
http: &'a Client,
guild_id: GuildId,
name: impl Into<String>,
) -> Result<Self, CreateGuildChannelError> {
Self::_new(http, guild_id, name.into())
}
fn _new(
http: &'a Client,
guild_id: GuildId,
name: String,
) -> Result<Self, CreateGuildChannelError> {
if !validate::channel_name(&name) {
return Err(CreateGuildChannelError {
kind: CreateGuildChannelErrorType::NameInvalid { name },
});
}
Ok(Self {
fields: CreateGuildChannelFields {
bitrate: None,
kind: None,
name,
nsfw: None,
parent_id: None,
permission_overwrites: None,
position: None,
rate_limit_per_user: None,
topic: None,
user_limit: None,
},
fut: None,
guild_id,
http,
reason: None,
})
}
pub fn bitrate(mut self, bitrate: u64) -> Self {
self.fields.bitrate.replace(bitrate);
self
}
pub fn kind(mut self, kind: ChannelType) -> Self {
self.fields.kind.replace(kind);
self
}
pub fn nsfw(mut self, nsfw: bool) -> Self {
self.fields.nsfw.replace(nsfw);
self
}
pub fn parent_id(mut self, parent_id: impl Into<ChannelId>) -> Self {
self.fields.parent_id.replace(parent_id.into());
self
}
pub fn permission_overwrites(
mut self,
permission_overwrites: Vec<PermissionOverwrite>,
) -> Self {
self.fields
.permission_overwrites
.replace(permission_overwrites);
self
}
pub fn position(mut self, position: u64) -> Self {
self.fields.position.replace(position);
self
}
pub fn rate_limit_per_user(
mut self,
rate_limit_per_user: u64,
) -> Result<Self, CreateGuildChannelError> {
if rate_limit_per_user > 21600 {
return Err(CreateGuildChannelError {
kind: CreateGuildChannelErrorType::RateLimitPerUserInvalid {
rate_limit_per_user,
},
});
}
self.fields.rate_limit_per_user.replace(rate_limit_per_user);
Ok(self)
}
pub fn topic(self, topic: impl Into<String>) -> Result<Self, CreateGuildChannelError> {
self._topic(topic.into())
}
fn _topic(mut self, topic: String) -> Result<Self, CreateGuildChannelError> {
if topic.chars().count() > 1024 {
return Err(CreateGuildChannelError {
kind: CreateGuildChannelErrorType::TopicInvalid { topic },
});
}
self.fields.topic.replace(topic);
Ok(self)
}
pub fn user_limit(mut self, user_limit: u64) -> Self {
self.fields.user_limit.replace(user_limit);
self
}
fn start(&mut self) -> Result<(), HttpError> {
let mut request = Request::builder(Route::CreateChannel {
guild_id: self.guild_id.0,
})
.json(&self.fields)?;
if let Some(reason) = self.reason.as_ref() {
request = request.headers(request::audit_header(reason)?);
}
self.fut
.replace(Box::pin(self.http.request(request.build())));
Ok(())
}
}
impl<'a> AuditLogReason for CreateGuildChannel<'a> {
fn reason(mut self, reason: impl Into<String>) -> Result<Self, AuditLogReasonError> {
self.reason
.replace(AuditLogReasonError::validate(reason.into())?);
Ok(self)
}
}
poll_req!(CreateGuildChannel<'_>, GuildChannel);