dynamic_proxy_macros/
lib.rs1use 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]
43pub 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 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}