Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow destructuring in command parameters #217

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading