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()
}