Skip to content

Commit

Permalink
Allow destructuring in command parameters (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattfbacon authored Nov 17, 2023
1 parent 695ae1d commit e6a7337
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 24 deletions.
27 changes: 13 additions & 14 deletions macros/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ struct ParamArgs {

/// Part of the Invocation struct. Represents a single parameter of a Discord command.
struct CommandParameter {
ident: syn::Ident,
name: String,
type_: syn::Type,
args: ParamArgs,
Expand Down Expand Up @@ -162,18 +161,14 @@ pub fn command(
// Collect argument names/types/attributes to insert into generated function
let mut parameters = Vec::new();
for command_param in function.sig.inputs.iter_mut().skip(1) {
let span = command_param.span();

let pattern = match command_param {
syn::FnArg::Typed(x) => &mut *x,
syn::FnArg::Typed(x) => x,
syn::FnArg::Receiver(r) => {
return Err(syn::Error::new(r.span(), "self argument is invalid here").into());
}
};
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())
}
};

let attrs = pattern
.attrs
Expand All @@ -182,15 +177,19 @@ pub fn command(
.collect::<Result<Vec<_>, _>>()?;
let attrs = <ParamArgs as darling::FromMeta>::from_list(&attrs)?;

let name = if let Some(rename) = &attrs.rename {
rename.clone()
} else if let syn::Pat::Ident(ident) = &*pattern.pat {
ident.ident.to_string().trim_start_matches("r#").into()
} else {
let message = "#[rename = \"...\"] must be specified for pattern parameters";
return Err(syn::Error::new(pattern.pat.span(), message).into());
};
parameters.push(CommandParameter {
ident: ident.clone(),
name: attrs
.rename
.clone()
.unwrap_or_else(|| ident.to_string().trim_start_matches("r#").to_string()),
name,
type_: (*pattern.ty).clone(),
args: attrs,
span: command_param.span(),
span,
});
}

Expand Down
17 changes: 8 additions & 9 deletions macros/src/command/prefix.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::Invocation;
use quote::format_ident;
use syn::spanned::Spanned as _;

fn quote_parameter(p: &super::CommandParameter) -> Result<proc_macro2::TokenStream, syn::Error> {
Expand All @@ -14,23 +15,20 @@ fn quote_parameter(p: &super::CommandParameter) -> Result<proc_macro2::TokenStre
(false, true, false) => Modifier::Rest,
(false, false, true) => Modifier::Flag,
_ => {
return Err(syn::Error::new(
p.span,
"modifiers like #[lazy] or #[rest] currently cannot be used together",
)
.into())
let message = "modifiers like #[lazy] or #[rest] currently cannot be used together";
return Err(syn::Error::new(p.span, message));
}
};
let type_ = &p.type_;
Ok(match modifier {
Modifier::Flag => {
if p.type_ != syn::parse_quote! { bool } {
return Err(syn::Error::new(p.type_.span(), "Must use bool for flags").into());
return Err(syn::Error::new(p.type_.span(), "Must use bool for flags"));
}
// 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());
let literal = proc_macro2::Literal::string(&p.name);
quote::quote! { #[flag] (#literal) }
}
Modifier::Lazy => quote::quote! { #[lazy] (#type_) },
Expand All @@ -40,7 +38,9 @@ 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_idents = inv.parameters.iter().map(|p| &p.ident).collect::<Vec<_>>();
let param_idents = (0..inv.parameters.len())
.map(|i| format_ident!("poise_param_{i}"))
.collect::<Vec<_>>();
let param_specs = inv
.parameters
.iter()
Expand All @@ -52,7 +52,6 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre
};

Ok(quote::quote! {
#[allow(clippy::used_underscore_binding)]
|ctx| Box::pin(async move {
let ( #( #param_idents, )* .. ) = ::poise::parse_prefix_args!(
ctx.serenity_context, ctx.msg, ctx.args, 0 =>
Expand Down
5 changes: 4 additions & 1 deletion macros/src/command/slash.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::Invocation;
use crate::util::extract_type_parameter;
use quote::format_ident;
use syn::spanned::Spanned as _;

pub fn generate_parameters(inv: &Invocation) -> Result<Vec<proc_macro2::TokenStream>, syn::Error> {
Expand Down Expand Up @@ -143,7 +144,9 @@ pub fn generate_slash_action(inv: &Invocation) -> Result<proc_macro2::TokenStrea
}
}

let param_identifiers = inv.parameters.iter().map(|p| &p.ident).collect::<Vec<_>>();
let param_identifiers = (0..inv.parameters.len())
.map(|i| format_ident!("poise_param_{i}"))
.collect::<Vec<_>>();
let param_names = inv.parameters.iter().map(|p| &p.name).collect::<Vec<_>>();

let param_types = inv
Expand Down

0 comments on commit e6a7337

Please sign in to comment.