robespierre_fw_macros/
lib.rs1use proc_macro2::TokenStream;
2use quote::{format_ident, quote, ToTokens};
3use syn::{
4 parse::Parse, parse_macro_input, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg,
5 Type,
6};
7
8#[allow(clippy::borrowed_box)]
9struct ExtraArgs<'a>(&'a [&'a Box<Type>], &'a [Vec<Attribute>]);
10
11impl<'a> ToTokens for ExtraArgs<'a> {
12 fn to_tokens(&self, tokens: &mut TokenStream) {
13 for (ty, attrs) in self.0.iter().zip(self.1.iter()) {
14 let ty_from_message =
15 quote! {<#ty as ::robespierre::framework::standard::extractors::FromMessage>};
16 let config_ty = quote! {#ty_from_message ::Config};
17 let config_ty_ufcs_root = quote! {<#config_ty as ::robespierre::framework::standard::extractors::ExtractorConfigBuilder>};
18 let config_builder = {
19 let tks = quote! {<#config_ty as ::std::default::Default>::default()};
20
21 let tks = attrs.iter().fold(tks, |tks, attr| {
22 if attr.path.is_ident("delimiter") {
23 let expr = attr.parse_args::<Expr>().expect("parse as expr");
24 quote! { #config_ty_ufcs_root :: delimiter(#tks, #expr)}
25 } else if attr.path.is_ident("delimiters") {
26 struct DelimiterList(Punctuated<Expr, Comma>);
27
28 impl Parse for DelimiterList {
29 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
30 Punctuated::<Expr, Comma>::parse_terminated(input).map(Self)
31 }
32 }
33
34 let delimiters = attr
35 .parse_args::<DelimiterList>()
36 .expect("parse as delimiter list")
37 .0;
38 quote! { #config_ty_ufcs_root :: delimiters(#tks, ::std::vec![#delimiters])}
39 } else {
40 panic!("Unknown attribute: {:?}", attr.path);
41 }
42 });
43
44 tks
45 };
46 tokens.extend(quote! {
47 #ty_from_message ::from_message(ctx.clone(), __message.clone(), #config_builder).await?,
48 });
49 }
50 }
51}
52
53#[proc_macro_attribute]
68pub fn command(
69 _attr: proc_macro::TokenStream,
70 item: proc_macro::TokenStream,
71) -> proc_macro::TokenStream {
72 let mut command_func = parse_macro_input!(item as syn::ItemFn);
73
74 let impl_name = format_ident!("__{}_impl", &command_func.sig.ident);
75 let old_name = std::mem::replace(&mut command_func.sig.ident, impl_name.clone());
76
77 let visibility = std::mem::replace(&mut command_func.vis, syn::Visibility::Inherited);
78
79 let attrs = command_func
80 .sig
81 .inputs
82 .iter_mut()
83 .skip(2)
84 .filter_map(|it| match it {
85 FnArg::Typed(t) => {
86 let (my_attrs, other_attrs): (Vec<_>, _) =
87 std::mem::take(&mut t.attrs).into_iter().partition(|attr| {
88 attr.path.is_ident("delimiter") || attr.path.is_ident("delimiters")
89 });
90 t.attrs = other_attrs;
91
92 Some(my_attrs)
93 }
94 FnArg::Receiver(_) => None,
95 })
96 .collect::<Vec<_>>();
97
98 let extra_args = command_func
99 .sig
100 .inputs
101 .iter()
102 .skip(2)
103 .filter_map(|it| match it {
104 FnArg::Typed(a) => Some(&a.ty),
105 FnArg::Receiver(_) => None,
106 })
107 .collect::<Vec<_>>();
108
109 let extra_args = ExtraArgs(&extra_args, &attrs);
110
111 let result = quote! {
112 #visibility fn #old_name <'a> (
113 ctx: &'a ::robespierre::framework::standard::FwContext,
114 message: &'a ::std::sync::Arc<::robespierre_models::channels::Message>,
115 args: &'a ::std::primitive::str,
116 ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::robespierre::framework::standard::CommandResult> + ::std::marker::Send + 'a>> {
117 #command_func
118
119 ::std::boxed::Box::pin(async move {
120 let __message = ::robespierre::framework::standard::extractors::Msg {message: ::std::sync::Arc::clone(message), args: ::std::sync::Arc::new(args.to_string())};
121 #impl_name (
122 ctx,
123 message,
124 #extra_args
125 ).await
126 })
127 }
128 };
129 proc_macro::TokenStream::from(result)
130}