#[cfg(test)]
mod tests;
use twilight_model::{
application::{
command::CommandOptionType,
interaction::application_command::{CommandDataOption, CommandOptionValue},
},
id::{
Id,
marker::{AttachmentMarker, ChannelMarker, GenericMarker, RoleMarker, UserMarker},
},
};
impl ExtractOptionValue for CommandOptionValue {
fn attachment(&self) -> Option<Id<AttachmentMarker>> {
if let Self::Attachment(attachment_id) = self {
Some(*attachment_id)
} else {
None
}
}
fn boolean(&self) -> Option<bool> {
if let Self::Boolean(val) = self {
Some(*val)
} else {
None
}
}
fn channel(&self) -> Option<Id<ChannelMarker>> {
if let Self::Channel(val) = self {
Some(*val)
} else {
None
}
}
fn focused(self) -> Option<(String, CommandOptionType)> {
if let Self::Focused(name, option_type) = self {
Some((name, option_type))
} else {
None
}
}
fn integer(&self) -> Option<i64> {
if let Self::Integer(data) = self {
Some(*data)
} else {
None
}
}
fn mentionable(&self) -> Option<Id<GenericMarker>> {
if let Self::Mentionable(data) = self {
Some(*data)
} else {
None
}
}
fn number(&self) -> Option<f64> {
if let Self::Number(data) = self {
Some(*data)
} else {
None
}
}
fn role(&self) -> Option<Id<RoleMarker>> {
if let Self::Role(data) = self {
Some(*data)
} else {
None
}
}
fn string(self) -> Option<String> {
if let Self::String(data) = self {
Some(data)
} else {
None
}
}
fn subcommand(self) -> Option<Vec<CommandDataOption>> {
if let Self::SubCommand(data) = self {
Some(data)
} else {
None
}
}
fn subcommand_group(self) -> Option<Vec<CommandDataOption>> {
if let Self::SubCommandGroup(data) = self {
Some(data)
} else {
None
}
}
fn user(&self) -> Option<Id<UserMarker>> {
if let Self::User(data) = self {
Some(*data)
} else {
None
}
}
}
pub trait ExtractOption {
fn option(self, name: &str) -> Option<CommandOptionValue>;
}
pub trait ExtractOptionValue {
fn attachment(&self) -> Option<Id<AttachmentMarker>>;
fn boolean(&self) -> Option<bool>;
fn channel(&self) -> Option<Id<ChannelMarker>>;
fn focused(self) -> Option<(String, CommandOptionType)>;
fn integer(&self) -> Option<i64>;
fn mentionable(&self) -> Option<Id<GenericMarker>>;
fn number(&self) -> Option<f64>;
fn role(&self) -> Option<Id<RoleMarker>>;
fn string(self) -> Option<String>;
fn subcommand(self) -> Option<Vec<CommandDataOption>>;
fn subcommand_group(self) -> Option<Vec<CommandDataOption>>;
fn user(&self) -> Option<Id<UserMarker>>;
}
impl ExtractOption for Vec<CommandDataOption> {
fn option(self, name: &str) -> Option<CommandOptionValue> {
let (option_name, subcommand_name_opt, subcommand_group_name_opt) = parse_option_name(name);
if let (Some(subcommand_group_name), Some(subcommand_name)) =
(subcommand_group_name_opt, subcommand_name_opt)
{
let subcommand_group_options =
find_option(self, subcommand_group_name)?.subcommand_group()?;
let subcommand_options =
find_option(subcommand_group_options, subcommand_name)?.subcommand()?;
find_option(subcommand_options, option_name)
} else if let Some(subcommand_name) = subcommand_name_opt {
let subcommand_options = find_option(self, subcommand_name)?.subcommand()?;
find_option(subcommand_options, option_name)
} else {
find_option(self, option_name)
}
}
}
fn find_option(options: Vec<CommandDataOption>, name: &str) -> Option<CommandOptionValue> {
options
.into_iter()
.find_map(|option| (option.name == name).then_some(option.value))
}
fn parse_option_name(name: &str) -> (&str, Option<&str>, Option<&str>) {
let mut name_parts = name.split('/').rev();
let option_name = name_parts.next().unwrap_or(name);
let subcommand_name_opt = name_parts.next();
let subcommand_group_name_opt = name_parts.next();
(option_name, subcommand_name_opt, subcommand_group_name_opt)
}