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
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| {
        quote! {
            if method.as_str() == #p::NAME {
                let msg = <#p as Service>::read(data).map_err(|_| quix::derive::DispatchError::Format);
                let run = async move {
                    let res = addr.send(msg?).await.map_err(|_| quix::derive::DispatchError::MailboxRemote)?;
                    let mut buf = quix::derive::BytesMut::new();
                    quix::derive::ProstMessage::encode(&res, &mut buf).map_err(|_| quix::derive::DispatchError::Format)?;
                    Ok(buf.freeze())
                };
                return Box::pin(run);
            }
        }
    });

    let name = i.ident;
    let tokens = quote! {
        impl quix::derive::ProcessDispatch for #name {
            fn make_dispatcher(addr: actix::WeakAddr<Self>) -> Box<dyn quix::derive::Dispatcher> {
                pub struct LocalDispatcher { addr: actix::WeakAddr<#name> }
                impl quix::derive::Dispatcher for LocalDispatcher {
                    fn dispatch(&self, method: String, 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();
                        #(#messages)*
                        return Box::pin(async move { Err(quix::derive::DispatchError::DispatchRemote)})
                    }
                }
                return Box::new(LocalDispatcher { addr });
            }
        }
    };

    tokens.into()
}