fcplug_macros/
lib.rs

1#![feature(box_patterns)]
2
3use proc_macro::TokenStream;
4
5use proc_macro2::{Ident, Span};
6use quote::quote;
7use syn::{ItemFn, ReturnType, Type, TypePath, TypeTuple};
8
9/// Register an FFI method that communicates using a custom protocol.
10/// format description: `#[ffi_raw_method]`
11/// command to check expanded code: `cargo +nightly rustc -- -Zunstable-options
12/// --pretty=expanded`
13#[proc_macro_attribute]
14#[cfg(not(test))] // Work around for rust-lang/rust#62127
15pub fn ffi_raw_method(_args: TokenStream, item: TokenStream) -> TokenStream {
16    let raw_item = proc_macro2::TokenStream::from(item.clone());
17    let raw_sig = syn::parse_macro_input!(item as ItemFn).sig;
18    let raw_ident = raw_sig.ident;
19    let new_ident = Ident::new(&format!("ffi_raw_{}", raw_ident), Span::call_site());
20    let new_item = quote! {
21        #[inline]
22        #[no_mangle]
23        pub extern "C" fn #new_ident(mut req: ::fcplug_callee::Buffer) -> ::fcplug_callee::FFIResult {
24            #raw_item
25            ::fcplug_callee::callback(::std::stringify!(#new_ident), #raw_ident, &mut req)
26        }
27    };
28    TokenStream::from(new_item)
29}
30
31/// Register an FFI method that communicates using a protobuf protocol.
32/// format description: `#[ffi_pb_method]`
33/// command to check expanded code: `cargo +nightly rustc -- -Zunstable-options
34/// --pretty=expanded`
35#[proc_macro_attribute]
36#[cfg(not(test))] // Work around for rust-lang/rust#62127
37pub fn ffi_pb_method(_args: TokenStream, item: TokenStream) -> TokenStream {
38    let raw_item = proc_macro2::TokenStream::from(item.clone());
39    let raw_sig = syn::parse_macro_input!(item as ItemFn).sig;
40    let raw_ident = raw_sig.ident;
41    let new_ident = Ident::new(&format!("ffi_pb_{}", raw_ident), Span::call_site());
42    let new_item = quote! {
43        #[inline]
44        #[no_mangle]
45        pub extern "C" fn #new_ident(mut req: ::fcplug_callee::Buffer) -> ::fcplug_callee::FFIResult {
46            #raw_item
47            ::fcplug_callee::protobuf::callback(::std::stringify!(#new_ident), #raw_ident, &mut req)
48        }
49    };
50    TokenStream::from(new_item)
51}
52
53
54/// Register an FFI method that communicates using a flatbuffer protocol.
55/// format description: `#[ffi_fb_method]`
56/// command to check expanded code: `cargo +nightly rustc -- -Zunstable-options
57/// --pretty=expanded`
58#[proc_macro_attribute]
59#[cfg(not(test))] // Work around for rust-lang/rust#62127
60pub fn ffi_fb_method(_args: TokenStream, item: TokenStream) -> TokenStream {
61    const FORMAT: &'static str = "fn ${FN_NAME}<'a>(req: FbRequest<'a, EchoRequest<'a>>) -> (${RESPONSE_TYPE}Args<'a>, FbResponseWriter<${RESPONSE_TYPE}<'a>>){}";
62    let format_msg = format!("The function signature format must satisfy: {}", FORMAT);
63    let raw_item = proc_macro2::TokenStream::from(item.clone());
64    let raw_sig = syn::parse_macro_input!(item as ItemFn).sig;
65    let raw_ident = raw_sig.ident;
66    let new_ident = Ident::new(&format!("ffi_fb_{}", raw_ident), Span::call_site());
67    // TODO: improve
68    let resp_type = match raw_sig.output {
69        ReturnType::Type(_, box Type::Tuple(TypeTuple { elems, .. })) => {
70            if let Type::Path(TypePath { path, .. }) = elems.first().expect(&format_msg) {
71                let ident = format!("{}", path.segments.first().expect(&format_msg).ident);
72                if ident.ends_with("Args") {
73                    Ident::new(ident.strip_suffix("Args").expect(&format_msg), Span::call_site())
74                } else {
75                    panic!("{}", format_msg)
76                }
77            } else {
78                panic!("{}", format_msg)
79            }
80        }
81        _ => panic!("{}", format_msg)
82    };
83
84    let new_item = quote! {
85        #[inline]
86        #[no_mangle]
87        pub extern "C" fn #new_ident(mut req: ::fcplug_callee::Buffer) -> ::fcplug_callee::FFIResult {
88            #raw_item
89            let (_resp_, mut _w_) = #raw_ident(::fcplug_callee::flatbuf::FbRequest::try_from_buffer(&mut req)?);
90            let _resp_ = #resp_type::create(&mut _w_, &_resp_);
91            _w_.finish_minimal(_resp_);
92            ::fcplug_callee::FFIResult::ok(::fcplug_callee::ABIResponse::try_into_buffer(_w_).unwrap())
93        }
94    };
95    TokenStream::from(new_item)
96}