quix_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::{TokenStream};
4
5use syn::{DeriveInput, Attribute, Type};
6use quote::quote;
7use syn::parse::{Parse, ParseBuffer};
8use syn::Token;
9
10struct TypeList {
11    paths: Vec<Type>
12}
13
14impl Parse for TypeList {
15    fn parse(input: &ParseBuffer) -> syn::Result<Self> {
16        let content;
17        let mut res = vec![];
18        syn::parenthesized!(content in input);
19        loop {
20            res.push(content.parse()?);
21            if content.peek(Token![,]) {
22                let _: Token![,] = content.parse()?;
23            } else {
24                break;
25            }
26        }
27        Ok(Self {
28            paths: res
29        })
30    }
31}
32
33#[proc_macro_derive(ProcessDispatch, attributes(dispatch))]
34pub fn my_derive(_input: TokenStream) -> TokenStream {
35    let mut i: DeriveInput = syn::parse(_input).unwrap();
36    let attr: Attribute = i.attrs.pop().unwrap();
37    let paths = if attr.path.get_ident().map(|i| i.to_string()).as_deref() == Some("dispatch") {
38        let list: TypeList = syn::parse2(attr.tokens).unwrap();
39        list.paths
40    } else {
41        panic!("Missing paths")
42    };
43    let messages = paths.into_iter().map(|p| {
44        let id = quote! { #p::ID };
45        let block = quote! {
46        {
47            let msg = <#p as Service>::read(data).map_err(|_| quix::derive::DispatchError::Format);
48            Box::pin(async move {
49                let res = addr.send(msg?).await.map_err(|_| quix::derive::DispatchError::MailboxRemote)?;
50                let mut buf = quix::derive::BytesMut::new();
51                <#p as Service>::write_result(&res, &mut buf).map_err(|_| quix::derive::DispatchError::Format)?;
52                Ok(buf.freeze())
53            })
54        }
55        };
56
57        quote! { #id => #block }
58    });
59
60
61    let dispatch = quote! {
62        match method {
63            #(#messages),*
64            _ => {
65                Box::pin(async move { Err(quix::derive::DispatchError::DispatchRemote)})
66            }
67        }
68    };
69
70    let name = i.ident;
71
72    let dispatcher = quote! {
73        pub struct LocalDispatcher { addr: actix::WeakAddr<#name> }
74        impl quix::derive::Dispatcher for LocalDispatcher {
75            fn dispatch(&self, method: u64, data: quix::derive::Bytes) -> quix::derive::BoxFuture<'static, Result<quix::derive::Bytes, quix::derive::DispatchError>> {
76                use quix::derive::Service;
77                let addr = self.addr.upgrade().unwrap().clone();
78                #dispatch
79            }
80        }
81        Box::new(LocalDispatcher { addr })
82    };
83
84    let tokens = quote! {
85        impl quix::derive::ProcessDispatch for #name {
86            fn make_dispatcher(addr: actix::WeakAddr<Self>) -> Box<dyn quix::derive::Dispatcher> {
87                #dispatcher
88            }
89        }
90    };
91
92    tokens.into()
93}