use twilight_model::{
application::command::{
Command, CommandOption, CommandOptionChoice, CommandOptionChoiceData, CommandOptionType,
CommandOptionValue, CommandType,
},
channel::ChannelType,
guild::Permissions,
id::{marker::GuildMarker, Id},
};
use twilight_validate::command::{command as validate_command, CommandValidationError};
#[derive(Clone, Debug)]
#[must_use = "must be built into a command"]
pub struct CommandBuilder(Command);
impl CommandBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>, kind: CommandType) -> Self {
Self(Command {
application_id: None,
default_member_permissions: None,
dm_permission: None,
description: description.into(),
description_localizations: None,
guild_id: None,
id: None,
kind,
name: name.into(),
name_localizations: None,
nsfw: None,
options: Vec::new(),
version: Id::new(1),
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "must be built into a command"]
pub fn build(self) -> Command {
self.0
}
pub fn validate(self) -> Result<Self, CommandValidationError> {
validate_command(&self.0)?;
Ok(self)
}
pub const fn guild_id(mut self, guild_id: Id<GuildMarker>) -> Self {
self.0.guild_id = Some(guild_id);
self
}
pub const fn default_member_permissions(
mut self,
default_member_permissions: Permissions,
) -> Self {
self.0.default_member_permissions = Some(default_member_permissions);
self
}
pub const fn dm_permission(mut self, dm_permission: bool) -> Self {
self.0.dm_permission = Some(dm_permission);
self
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn option(self, option: impl Into<CommandOption>) -> Self {
self._option(option.into())
}
fn _option(mut self, option: CommandOption) -> Self {
self.0.options.push(option);
self
}
pub const fn nsfw(mut self, nsfw: bool) -> Self {
self.0.nsfw = Some(nsfw);
self
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct AttachmentBuilder(CommandOption);
impl AttachmentBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Attachment,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<AttachmentBuilder> for CommandOption {
fn from(builder: AttachmentBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct BooleanBuilder(CommandOption);
impl BooleanBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Boolean,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<BooleanBuilder> for CommandOption {
fn from(builder: BooleanBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct ChannelBuilder(CommandOption);
impl ChannelBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: Some(Vec::new()),
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Channel,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn channel_types(mut self, channel_types: impl IntoIterator<Item = ChannelType>) -> Self {
self.0.channel_types = Some(Vec::from_iter(channel_types));
self
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<ChannelBuilder> for CommandOption {
fn from(builder: ChannelBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct IntegerBuilder(CommandOption);
impl IntegerBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: Some(false),
channel_types: None,
choices: Some(Vec::new()),
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Integer,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
self.0.autocomplete = Some(autocomplete);
self
}
#[track_caller]
pub fn choice_localizations<K: Into<String>, V: Into<String>>(
mut self,
choice_name: &str,
name_localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
let choice = self.0.choices.as_mut().expect("choices are set").iter_mut().find(
|choice| matches!(choice, CommandOptionChoice::Integer(CommandOptionChoiceData{ name, ..}) if name == choice_name),
);
if let Some(choice) = choice {
set_choice_localizations(choice, name_localizations);
}
self
}
pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, i64)>) -> Self {
self.0.choices = Some(
choices
.into_iter()
.map(|(name, value, ..)| {
CommandOptionChoice::Integer(CommandOptionChoiceData {
name: name.into(),
name_localizations: None,
value,
})
})
.collect(),
);
self
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn max_value(mut self, value: i64) -> Self {
self.0.max_value = Some(CommandOptionValue::Integer(value));
self
}
pub const fn min_value(mut self, value: i64) -> Self {
self.0.min_value = Some(CommandOptionValue::Integer(value));
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<IntegerBuilder> for CommandOption {
fn from(builder: IntegerBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct MentionableBuilder(CommandOption);
impl MentionableBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Mentionable,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<MentionableBuilder> for CommandOption {
fn from(builder: MentionableBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct NumberBuilder(CommandOption);
impl NumberBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: Some(false),
channel_types: None,
choices: Some(Vec::new()),
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Number,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
self.0.autocomplete = Some(autocomplete);
self
}
#[track_caller]
pub fn choice_localizations<K: Into<String>, V: Into<String>>(
mut self,
choice_name: &str,
name_localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
let choice = self.0.choices.as_mut().expect("choices are set").iter_mut().find(
|choice| matches!(choice, CommandOptionChoice::Number(CommandOptionChoiceData {name, ..}) if name == choice_name),
);
if let Some(choice) = choice {
set_choice_localizations(choice, name_localizations);
}
self
}
pub fn choices<K: Into<String>>(mut self, choices: impl IntoIterator<Item = (K, f64)>) -> Self {
self.0.choices = Some(
choices
.into_iter()
.map(|(name, value, ..)| {
CommandOptionChoice::Number(CommandOptionChoiceData {
name: name.into(),
name_localizations: None,
value,
})
})
.collect(),
);
self
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn max_value(mut self, value: f64) -> Self {
self.0.max_value = Some(CommandOptionValue::Number(value));
self
}
pub const fn min_value(mut self, value: f64) -> Self {
self.0.min_value = Some(CommandOptionValue::Number(value));
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<NumberBuilder> for CommandOption {
fn from(builder: NumberBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct RoleBuilder(CommandOption);
impl RoleBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::Role,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<RoleBuilder> for CommandOption {
fn from(builder: RoleBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct StringBuilder(CommandOption);
impl StringBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: Some(false),
channel_types: None,
choices: Some(Vec::new()),
description: description.into(),
description_localizations: None,
kind: CommandOptionType::String,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub const fn autocomplete(mut self, autocomplete: bool) -> Self {
self.0.autocomplete = Some(autocomplete);
self
}
#[track_caller]
pub fn choice_localizations<K: Into<String>, V: Into<String>>(
mut self,
choice_name: &str,
name_localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
let choice = self.0.choices.as_mut().expect("choices are set").iter_mut().find(
|choice| matches!(choice, CommandOptionChoice::String(CommandOptionChoiceData{name, ..}) if name == choice_name),
);
if let Some(choice) = choice {
set_choice_localizations(choice, name_localizations);
}
self
}
pub fn choices<K: Into<String>, V: Into<String>>(
mut self,
choices: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.choices = Some(
choices
.into_iter()
.map(|(name, value, ..)| {
CommandOptionChoice::String(CommandOptionChoiceData {
name: name.into(),
name_localizations: None,
value: value.into(),
})
})
.collect(),
);
self
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn max_length(mut self, value: u16) -> Self {
self.0.max_length = Some(value);
self
}
pub const fn min_length(mut self, value: u16) -> Self {
self.0.min_length = Some(value);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<StringBuilder> for CommandOption {
fn from(builder: StringBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct SubCommandBuilder(CommandOption);
impl SubCommandBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::SubCommand,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: Some(Vec::new()),
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn option(self, option: impl Into<CommandOption>) -> Self {
self._option(option.into())
}
fn _option(mut self, option: CommandOption) -> Self {
self.0
.options
.as_mut()
.expect("set to Some in `new`")
.push(option);
self
}
}
impl From<SubCommandBuilder> for CommandOption {
fn from(builder: SubCommandBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct SubCommandGroupBuilder(CommandOption);
impl SubCommandGroupBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::SubCommandGroup,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: Some(Vec::new()),
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn subcommands(mut self, subcommands: impl IntoIterator<Item = SubCommandBuilder>) -> Self {
self.0.options = Some(subcommands.into_iter().map(Into::into).collect());
self
}
}
impl From<SubCommandGroupBuilder> for CommandOption {
fn from(builder: SubCommandGroupBuilder) -> CommandOption {
builder.build()
}
}
#[derive(Clone, Debug)]
#[must_use = "should be used in a command builder"]
pub struct UserBuilder(CommandOption);
impl UserBuilder {
#[must_use = "builders have no effect if unused"]
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self(CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: description.into(),
description_localizations: None,
kind: CommandOptionType::User,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: name.into(),
name_localizations: None,
options: None,
required: None,
})
}
#[allow(clippy::missing_const_for_fn)]
#[must_use = "should be used in a command builder"]
pub fn build(self) -> CommandOption {
self.0
}
pub fn description_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.description_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub fn name_localizations<K: Into<String>, V: Into<String>>(
mut self,
localizations: impl IntoIterator<Item = (K, V)>,
) -> Self {
self.0.name_localizations = Some(
localizations
.into_iter()
.map(|(a, b)| (a.into(), b.into()))
.collect(),
);
self
}
pub const fn required(mut self, required: bool) -> Self {
self.0.required = Some(required);
self
}
}
impl From<UserBuilder> for CommandOption {
fn from(builder: UserBuilder) -> CommandOption {
builder.build()
}
}
fn set_choice_localizations<K: Into<String>, V: Into<String>>(
choice: &mut CommandOptionChoice,
localizations: impl IntoIterator<Item = (K, V)>,
) {
let name_localizations = match choice {
CommandOptionChoice::String(CommandOptionChoiceData {
name_localizations, ..
})
| CommandOptionChoice::Integer(CommandOptionChoiceData {
name_localizations, ..
})
| CommandOptionChoice::Number(CommandOptionChoiceData {
name_localizations, ..
}) => name_localizations,
};
*name_localizations = Some(
localizations
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect(),
);
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::assert_impl_all;
use std::fmt::Debug;
assert_impl_all!(AttachmentBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(CommandBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(BooleanBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(ChannelBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(IntegerBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(MentionableBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(RoleBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(StringBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(SubCommandBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(SubCommandGroupBuilder: Clone, Debug, Send, Sync);
assert_impl_all!(UserBuilder: Clone, Debug, Send, Sync);
#[test]
#[allow(clippy::too_many_lines)]
fn construct_command_with_builder() {
let command =
CommandBuilder::new(
"permissions",
"Get or edit permissions for a user or a role",
CommandType::ChatInput,
)
.nsfw(true)
.option(
SubCommandGroupBuilder::new("user", "Get or edit permissions for a user")
.subcommands([
SubCommandBuilder::new("get", "Get permissions for a user")
.option(UserBuilder::new("user", "The user to get").required(true))
.option(ChannelBuilder::new(
"channel",
"The channel permissions to get. If omitted, the guild \
permissions will be returned",
)),
SubCommandBuilder::new("edit", "Edit permissions for a user")
.option(UserBuilder::new("user", "The user to edit").required(true))
.option(ChannelBuilder::new(
"channel",
"The channel permissions to edit. If omitted, the guild \
permissions will be edited",
)),
]),
)
.option(
SubCommandGroupBuilder::new("role", "Get or edit permissions for a role")
.subcommands([
SubCommandBuilder::new("get", "Get permissions for a role")
.option(RoleBuilder::new("role", "The role to get").required(true))
.option(ChannelBuilder::new(
"channel",
"The channel permissions to get. If omitted, the guild \
permissions will be returned",
)),
SubCommandBuilder::new("edit", "Edit permissions for a role")
.option(RoleBuilder::new("role", "The role to edit").required(true))
.option(ChannelBuilder::new(
"channel",
"The channel permissions to edit. If omitted, the guild \
permissions will be edited",
)),
]),
)
.build();
let command_manual = Command {
application_id: None,
default_member_permissions: None,
dm_permission: None,
description: String::from("Get or edit permissions for a user or a role"),
guild_id: None,
id: None,
kind: CommandType::ChatInput,
name: String::from("permissions"),
name_localizations: None,
nsfw: Some(true),
description_localizations: None,
options: Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Get or edit permissions for a user".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommandGroup,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "user".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Get permissions for a user".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommand,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "get".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "The user to get".to_owned(),
description_localizations: None,
kind: CommandOptionType::User,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "user".to_owned(),
name_localizations: None,
options: None,
required: Some(true),
},
CommandOption {
autocomplete: None,
channel_types: Some(Vec::new()),
choices: None,
description:
"The channel permissions to get. If omitted, the guild \
permissions will be returned"
.to_owned(),
description_localizations: None,
kind: CommandOptionType::Channel,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "channel".to_owned(),
name_localizations: None,
options: None,
required: None,
},
])),
required: None,
},
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Edit permissions for a user".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommand,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "edit".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "The user to edit".to_owned(),
description_localizations: None,
kind: CommandOptionType::User,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "user".to_owned(),
name_localizations: None,
options: None,
required: Some(true),
},
CommandOption {
autocomplete: None,
channel_types: Some(Vec::new()),
choices: None,
description:
"The channel permissions to edit. If omitted, the guild \
permissions will be edited"
.to_owned(),
description_localizations: None,
kind: CommandOptionType::Channel,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "channel".to_owned(),
name_localizations: None,
options: None,
required: None,
},
])),
required: None,
},
])),
required: None,
},
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Get or edit permissions for a role".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommandGroup,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "role".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Get permissions for a role".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommand,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "get".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "The role to get".to_owned(),
description_localizations: None,
kind: CommandOptionType::Role,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "role".to_owned(),
name_localizations: None,
options: None,
required: Some(true),
},
CommandOption {
autocomplete: None,
channel_types: Some(Vec::new()),
choices: None,
description:
"The channel permissions to get. If omitted, the guild \
permissions will be returned"
.to_owned(),
description_localizations: None,
kind: CommandOptionType::Channel,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "channel".to_owned(),
name_localizations: None,
options: None,
required: None,
},
])),
required: None,
},
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "Edit permissions for a role".to_owned(),
description_localizations: None,
kind: CommandOptionType::SubCommand,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "edit".to_owned(),
name_localizations: None,
options: Some(Vec::from([
CommandOption {
autocomplete: None,
channel_types: None,
choices: None,
description: "The role to edit".to_owned(),
description_localizations: None,
kind: CommandOptionType::Role,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "role".to_owned(),
name_localizations: None,
options: None,
required: Some(true),
},
CommandOption {
autocomplete: None,
channel_types: Some(Vec::new()),
choices: None,
description:
"The channel permissions to edit. If omitted, the guild \
permissions will be edited"
.to_owned(),
description_localizations: None,
kind: CommandOptionType::Channel,
max_length: None,
max_value: None,
min_length: None,
min_value: None,
name: "channel".to_owned(),
name_localizations: None,
options: None,
required: None,
},
])),
required: None,
},
])),
required: None,
},
]),
version: Id::new(1),
};
assert_eq!(command, command_manual);
}
#[test]
fn validate() {
let result = CommandBuilder::new("", "", CommandType::ChatInput).validate();
assert!(result.is_err());
}
}