Skip to content

Commit

Permalink
Fix raw identifier command names and parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
kangalio committed Aug 31, 2023
1 parent aaa57f6 commit cfc1d42
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 39 deletions.
2 changes: 1 addition & 1 deletion examples/feature_showcase/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async fn main() {
parameter_attributes::say(),
parameter_attributes::punish(),
parameter_attributes::stringlen(),
// raw_identifiers::r#move(), // Currently doesn't work (issue #170)
raw_identifiers::r#move(),
response_with_reply::reply(),
subcommands::parent(),
subcommand_required::parent_subcommand_required(),
Expand Down
39 changes: 25 additions & 14 deletions macros/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ struct ParamArgs {

/// Part of the Invocation struct. Represents a single parameter of a Discord command.
struct CommandParameter {
name: syn::Ident,
ident: syn::Ident,
name: String,
type_: syn::Type,
args: ParamArgs,
span: proc_macro2::Span,
}

/// Passed to prefix and slash command spec generators; contains info to be included in command spec
pub struct Invocation {
command_name: String,
parameters: Vec<CommandParameter>,
description: Option<String>,
help_text: Option<String>,
Expand Down Expand Up @@ -168,7 +168,7 @@ pub fn command(
return Err(syn::Error::new(r.span(), "self argument is invalid here").into());
}
};
let name = match &*pattern.pat {
let ident = match &*pattern.pat {
syn::Pat::Ident(pat_ident) => &pat_ident.ident,
x => {
return Err(syn::Error::new(x.span(), "must use an identifier pattern here").into())
Expand All @@ -183,7 +183,11 @@ pub fn command(
let attrs = <ParamArgs as darling::FromMeta>::from_list(&attrs)?;

parameters.push(CommandParameter {
name: name.clone(),
ident: ident.clone(),
name: attrs
.rename
.clone()
.unwrap_or_else(|| ident.to_string().trim_start_matches("r#").to_string()),
type_: (*pattern.ty).clone(),
args: attrs,
span: command_param.span(),
Expand All @@ -209,10 +213,6 @@ pub fn command(
let required_bot_permissions = permissions_to_tokens(&args.required_bot_permissions);

let inv = Invocation {
command_name: args
.rename
.clone()
.unwrap_or_else(|| function.sig.ident.to_string()),
parameters,
description,
help_text,
Expand Down Expand Up @@ -252,13 +252,23 @@ fn generate_command(mut inv: Invocation) -> Result<proc_macro2::TokenStream, dar
None => None,
});

let function_name = inv
.function
.sig
.ident
.to_string()
.trim_start_matches("r#")
.to_string();
let identifying_name = inv
.args
.identifying_name
.clone()
.unwrap_or_else(|| inv.function.sig.ident.to_string());
let source_code_name = inv.function.sig.ident.to_string();
let command_name = &inv.command_name;
.unwrap_or_else(|| function_name.clone());
let command_name = &inv
.args
.rename
.clone()
.unwrap_or_else(|| function_name.clone());
let context_menu_name = wrap_option(inv.args.context_menu_command.as_ref());

let description = match &inv.description {
Expand Down Expand Up @@ -316,12 +326,13 @@ fn generate_command(mut inv: Invocation) -> Result<proc_macro2::TokenStream, dar
let description_localizations =
crate::util::vec_tuple_2_to_hash_map(inv.args.description_localized);

let function_name = std::mem::replace(&mut inv.function.sig.ident, syn::parse_quote! { inner });
let function_ident =
std::mem::replace(&mut inv.function.sig.ident, syn::parse_quote! { inner });
let function_visibility = &inv.function.vis;
let function = &inv.function;
Ok(quote::quote! {
#[allow(clippy::str_to_string)]
#function_visibility fn #function_name() -> ::poise::Command<
#function_visibility fn #function_ident() -> ::poise::Command<
<#ctx_type_with_static as poise::_GetGenerics>::U,
<#ctx_type_with_static as poise::_GetGenerics>::E,
> {
Expand All @@ -338,7 +349,7 @@ fn generate_command(mut inv: Invocation) -> Result<proc_macro2::TokenStream, dar
name_localizations: #name_localizations,
qualified_name: String::from(#command_name), // properly filled in later by Framework
identifying_name: String::from(#identifying_name),
source_code_name: String::from(#source_code_name),
source_code_name: String::from(#function_name),
category: #category,
description: #description,
description_localizations: #description_localizations,
Expand Down
11 changes: 7 additions & 4 deletions macros/src/command/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ fn quote_parameter(p: &super::CommandParameter) -> Result<proc_macro2::TokenStre
if p.type_ != syn::parse_quote! { bool } {
return Err(syn::Error::new(p.type_.span(), "Must use bool for flags").into());
}
let literal = proc_macro2::Literal::string(&p.name.to_string());
// TODO: doesn't work for r#keywords :( I cant be arsed to fix this rn because basically
// nobody uses this feature anyways and I'd have to go change the macro_rules macro to
// not accept non-quoted idents anymore
let literal = proc_macro2::Literal::string(&p.ident.to_string());
quote::quote! { #[flag] (#literal) }
}
Modifier::Lazy => quote::quote! { #[lazy] (#type_) },
Expand All @@ -37,7 +40,7 @@ fn quote_parameter(p: &super::CommandParameter) -> Result<proc_macro2::TokenStre
}

pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStream, syn::Error> {
let param_names = inv.parameters.iter().map(|p| &p.name).collect::<Vec<_>>();
let param_idents = inv.parameters.iter().map(|p| &p.ident).collect::<Vec<_>>();
let param_specs = inv
.parameters
.iter()
Expand All @@ -50,7 +53,7 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre

Ok(quote::quote! {
|ctx| Box::pin(async move {
let ( #( #param_names, )* .. ) = ::poise::parse_prefix_args!(
let ( #( #param_idents, )* .. ) = ::poise::parse_prefix_args!(
ctx.serenity_context, ctx.msg, ctx.args, 0 =>
#( #param_specs, )*
#wildcard_arg
Expand All @@ -64,7 +67,7 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.into());
}

inner(ctx.into(), #( #param_names, )* )
inner(ctx.into(), #( #param_idents, )* )
.await
.map_err(|error| poise::FrameworkError::Command {
error,
Expand Down
16 changes: 3 additions & 13 deletions macros/src/command/slash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ pub fn generate_parameters(inv: &Invocation) -> Result<Vec<proc_macro2::TokenStr
required = false;
}

let param_name = match &param.args.rename {
Some(rename) => rename.clone(),
None => param.name.to_string(),
};
let param_name = &param.name;
let name_locales = param.args.name_localized.iter().map(|x| &x.0);
let name_localized_values = param.args.name_localized.iter().map(|x| &x.1);
let description_locales = param.args.description_localized.iter().map(|x| &x.0);
Expand Down Expand Up @@ -146,15 +143,8 @@ pub fn generate_slash_action(inv: &Invocation) -> Result<proc_macro2::TokenStrea
}
}

let param_identifiers = inv.parameters.iter().map(|p| &p.name).collect::<Vec<_>>();
let param_names = inv
.parameters
.iter()
.map(|p| match &p.args.rename {
Some(rename) => syn::Ident::new(rename, p.name.span()),
None => p.name.clone(),
})
.collect::<Vec<_>>();
let param_identifiers = inv.parameters.iter().map(|p| &p.ident).collect::<Vec<_>>();
let param_names = inv.parameters.iter().map(|p| &p.name).collect::<Vec<_>>();

let param_types = inv
.parameters
Expand Down
14 changes: 7 additions & 7 deletions src/slash_argument/slash_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ impl std::error::Error for SlashArgError {
#[macro_export]
macro_rules! _parse_slash {
// Extract Option<T>
($ctx:ident, $interaction:ident, $args:ident => $name:ident: Option<$type:ty $(,)*>) => {
if let Some(arg) = $args.iter().find(|arg| arg.name == stringify!($name)) {
($ctx:ident, $interaction:ident, $args:ident => $name:literal: Option<$type:ty $(,)*>) => {
if let Some(arg) = $args.iter().find(|arg| arg.name == $name) {
let arg = arg.value
.as_ref()
.ok_or($crate::SlashArgError::CommandStructureMismatch("expected argument value"))?;
Expand All @@ -65,21 +65,21 @@ macro_rules! _parse_slash {

// Extract Vec<T> (delegating to Option<T> because slash commands don't support variadic
// arguments right now)
($ctx:ident, $interaction:ident, $args:ident => $name:ident: Vec<$type:ty $(,)*>) => {
($ctx:ident, $interaction:ident, $args:ident => $name:literal: Vec<$type:ty $(,)*>) => {
match $crate::_parse_slash!($ctx, $interaction, $args => $name: Option<$type>) {
Some(value) => vec![value],
None => vec![],
}
};

// Extract #[flag]
($ctx:ident, $interaction:ident, $args:ident => $name:ident: FLAG) => {
($ctx:ident, $interaction:ident, $args:ident => $name:literal: FLAG) => {
$crate::_parse_slash!($ctx, $interaction, $args => $name: Option<bool>)
.unwrap_or(false)
};

// Extract T
($ctx:ident, $interaction:ident, $args:ident => $name:ident: $($type:tt)*) => {
($ctx:ident, $interaction:ident, $args:ident => $name:literal: $($type:tt)*) => {
$crate::_parse_slash!($ctx, $interaction, $args => $name: Option<$($type)*>)
.ok_or($crate::SlashArgError::CommandStructureMismatch("a required argument is missing"))?
};
Expand All @@ -101,7 +101,7 @@ let args: &[serenity::CommandDataOption] = todo!();
let (arg1, arg2) = poise::parse_slash_args!(
&ctx, interaction,
args => (arg1: String), (arg2: Option<u32>)
args => ("arg1": String), ("arg2": Option<u32>)
).await?;
# Ok(()) }
Expand All @@ -110,7 +110,7 @@ let (arg1, arg2) = poise::parse_slash_args!(
#[macro_export]
macro_rules! parse_slash_args {
($ctx:expr, $interaction:expr, $args:expr => $(
( $name:ident: $($type:tt)* )
( $name:literal: $($type:tt)* )
),* $(,)? ) => {
async /* not move! */ {
use $crate::SlashArgumentHack;
Expand Down

0 comments on commit cfc1d42

Please sign in to comment.