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 87 88 89 90 91 92 93
extern crate proc_macro; use proc_macro::{TokenStream}; use syn::{DeriveInput, Attribute, Type}; use quote::quote; use syn::parse::{Parse, ParseBuffer}; use syn::Token; struct TypeList { paths: Vec<Type> } impl Parse for TypeList { fn parse(input: &ParseBuffer) -> syn::Result<Self> { let content; let mut res = vec![]; syn::parenthesized!(content in input); loop { res.push(content.parse()?); if content.peek(Token![,]) { let _: Token![,] = content.parse()?; } else { break; } } Ok(Self { paths: res }) } } #[proc_macro_derive(ProcessDispatch, attributes(dispatch))] pub fn my_derive(_input: TokenStream) -> TokenStream { let mut i: DeriveInput = syn::parse(_input).unwrap(); let attr: Attribute = i.attrs.pop().unwrap(); let paths = if attr.path.get_ident().map(|i| i.to_string()).as_deref() == Some("dispatch") { let list: TypeList = syn::parse2(attr.tokens).unwrap(); list.paths } else { panic!("Missing paths") }; let messages = paths.into_iter().map(|p| { let id = quote! { #p::ID }; let block = quote! { { let msg = <#p as Service>::read(data).map_err(|_| quix::derive::DispatchError::Format); Box::pin(async move { let res = addr.send(msg?).await.map_err(|_| quix::derive::DispatchError::MailboxRemote)?; let mut buf = quix::derive::BytesMut::new(); <#p as Service>::write_result(&res, &mut buf).map_err(|_| quix::derive::DispatchError::Format)?; Ok(buf.freeze()) }) } }; quote! { #id => #block } }); let dispatch = quote! { match method { #(#messages),* _ => { Box::pin(async move { Err(quix::derive::DispatchError::DispatchRemote)}) } } }; let name = i.ident; let dispatcher = quote! { pub struct LocalDispatcher { addr: actix::WeakAddr<#name> } impl quix::derive::Dispatcher for LocalDispatcher { fn dispatch(&self, method: u64, data: quix::derive::Bytes) -> quix::derive::BoxFuture<'static, Result<quix::derive::Bytes, quix::derive::DispatchError>> { use quix::derive::Service; let addr = self.addr.upgrade().unwrap().clone(); #dispatch } } Box::new(LocalDispatcher { addr }) }; let tokens = quote! { impl quix::derive::ProcessDispatch for #name { fn make_dispatcher(addr: actix::WeakAddr<Self>) -> Box<dyn quix::derive::Dispatcher> { #dispatcher } } }; tokens.into() }