1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
#![recursion_limit = "128"] extern crate proc_macro; use proc_macro::TokenStream; use syn::spanned::Spanned; use syn::AttributeArgs; use quote::{quote, quote_spanned}; use syn::{parse_macro_input, ItemFn, NestedMeta}; const COMMAND_PREFIX: &'static str = "nestor_command_handler_"; #[proc_macro_attribute] pub fn command(macro_args: TokenStream, item: TokenStream) -> TokenStream { let macro_args = parse_macro_input!(macro_args as AttributeArgs); let route = match macro_args.get(0) { Some(NestedMeta::Literal(lit)) => quote! { Some(#lit) }, _ => quote! { None }, }; let item = parse_macro_input!(item as ItemFn); let fn_name = &item.ident; let name = syn::Ident::new( &format!("{}{}", COMMAND_PREFIX, &item.ident), item.ident.span(), ); let args: Vec<_> = item .decl .inputs .iter() .map(|input| { let span = input.span(); quote_spanned!(span=> nestor::request::FromRequest::from_request(request)?) }) .collect(); let (span, ty) = match &item.decl.output { syn::ReturnType::Default => (item.decl.output.span(), quote! {()}), syn::ReturnType::Type(_, ty) => (ty.span(), quote! {#ty}), }; let function_call = if let Some(_) = item.asyncness { quote_spanned! {span=> { let fut = #fn_name(#(#args),*); async { let val = r#await!(fut); <#ty as IntoOutcome>::into_outcome(val) } }} } else { quote_spanned! {span => { let res = <#ty as IntoOutcome>::into_outcome(#fn_name(#(#args),*)); async {res} }} }; let result = quote! { #[allow(non_camel_case_types)] pub struct #name; nestor::inventory::submit!(#![crate = nestor] Box::new(#name) as Box<dyn nestor::handler::CommandHandler>); impl nestor::handler::CommandHandler for #name { fn route_id(&self) -> Option<&'static str> { #route } fn handle<'a, 'r>( &'a self, request: &'a nestor::request::Request<'r>, ) -> Result<std::pin::Pin<Box<std::future::Future<Output = nestor::response::Outcome> + 'a>>, nestor::Error> { use std::pin::Pin; use nestor::response::IntoOutcome; let fut = #function_call; Ok(Box::pin(fut)) } } #item }; result.into() }