dynamic_proxy_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{Block, ImplItemFn, ItemTrait, Meta, parse_macro_input, Pat, Stmt};
4use syn::punctuated::Punctuated;
5use core::default::Default;
6use std::ops::Deref;
7use std::time::SystemTime;
8use syn::token::Brace;
9use syn::TraitItem::Fn;
10use syn::Visibility::Inherited;
11use syn::FnArg::Typed;
12
13extern crate proc_macro;
14
15
16#[cfg(test)]
17mod tests {
18    #[test]
19    fn it_works() {
20        assert_eq!(4, 4);
21    }
22}
23
24fn setup_logger() -> Result<(), fern::InitError> {
25    fern::Dispatch::new()
26        .format(|out, message, record| {
27            out.finish(format_args!(
28                "[{} {} {}] {}",
29                humantime::format_rfc3339_seconds(SystemTime::now()),
30                record.level(),
31                record.target(),
32                message
33            ))
34        })
35        .level(log::LevelFilter::Warn)
36        .chain(std::io::stdout())
37        .chain(fern::log_file("output.log")?)
38        .apply()?;
39    Ok(())
40}
41
42#[proc_macro_attribute]
43// _metadata is argument provided to macro call and _input is code to which attribute like macro attaches
44pub fn dynamic_proxy(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
45    let input_struct =
46        parse_macro_input!(_metadata with Punctuated::<Meta, syn::Token![,]>::parse_terminated);
47    let input_trait = parse_macro_input!(_input as ItemTrait);
48    let inp = &input_trait;
49    let name = &input_trait.ident;
50    let imp = input_struct.first();
51    let _ = setup_logger();
52
53    // let tr = input_trait.to_token_stream();
54    let body = input_trait.items.iter().filter_map(|item| {
55        match item {
56            Fn(ti) => {
57                let func = ti.to_owned();
58                let signature = func.sig;
59                let is_async = signature.asyncness.is_some();
60                let func_name = signature.ident.to_string();
61                let args = signature.inputs.iter().filter_map(|a| {
62                    match a {
63                        Typed(t) => match t.clone().pat.deref() {
64                            Pat::Ident(id) => Some(id.clone().ident),
65                            _ => None
66                        },
67                        _ => None
68                    }
69                });
70                let arg_names = args.clone().map(|i| i.to_string());
71                let r = &signature.output;
72                let return_type = match r {
73                    syn::ReturnType::Type(_, t) => t.deref().to_token_stream(),
74                    _ => quote!(Any)
75                };
76                let stmt: Vec<Stmt> =
77                    match is_async
78                    {
79                        true => syn::parse_quote!(
80                    let mut invocation_info = InvocationInfo {
81                        func_name: #func_name,
82                        arg_names: &[#(#arg_names),*],
83                        arg_values: &[#(Box::new(#args)),*],
84                        return_type: TypeId::of::<#return_type>(),
85                        return_value: None
86                    };
87                    self.call_async(&mut invocation_info).await;
88                    return invocation_info.return_value.unwrap().downcast::<#return_type>().unwrap().deref().clone();
89                ),
90                        _ => syn::parse_quote!(
91                    let mut invocation_info = InvocationInfo {
92                        func_name: #func_name,
93                        arg_names: &[#(#arg_names),*],
94                        arg_values: &[#(Box::new(#args)),*],
95                        return_type: TypeId::of::<#return_type>(),
96                        return_value: None
97                    };
98                    self.call(&mut invocation_info);
99                    return invocation_info.return_value.unwrap().downcast::<#return_type>().unwrap().deref().clone();
100                )
101                    };
102                Some(ImplItemFn {
103                    attrs: func.attrs,
104                    vis: Inherited,
105                    defaultness: None,
106                    sig: signature,
107                    block: Block {
108                        brace_token: Brace::default(),
109                        stmts: stmt,
110                    },
111                })
112            }
113            &_ => None
114        }
115    });
116
117    TokenStream::from(quote! {
118        #inp
119        impl #name for #imp {
120            #(#body)*
121        }
122    })
123}