dynamic_proxy_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{Block, ImplItem, ImplItemFn, ItemTrait, Meta, parse_macro_input, Pat, Stmt, TraitItem, Type};
4use syn::punctuated::Punctuated;
5use core::default::Default;
6use std::ops::Deref;
7use syn::token::Brace;
8use syn::Visibility::Inherited;
9use syn::FnArg::Typed;
10
11extern crate proc_macro;
12
13#[proc_macro_attribute]
14pub fn dynamic_proxy(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
16 let input_struct =
17 parse_macro_input!(_metadata with Punctuated::<Meta, syn::Token![,]>::parse_terminated);
18 let input_trait = parse_macro_input!(_input as ItemTrait);
19 let inp = &input_trait;
20 let name = &input_trait.ident;
21 let (impl_generics, ty_generics, where_clause) = input_trait.generics.split_for_impl();
22 let imp = input_struct.first();
23
24 let body = input_trait.items.iter().filter_map(|item| {
26 match item {
27 TraitItem::Fn(ti) => {
28 let func = ti.to_owned();
29 let signature = func.sig;
30 let is_async = signature.asyncness.is_some();
31 let func_name = signature.ident.to_string();
32 let mut errors = Vec::new();
33 let args: Vec<_> = signature.inputs.iter().filter_map(|a| {
34 match a {
35 Typed(t) => match t.clone().pat.deref() {
36 Pat::Ident(id) => {
37 if matches!(t.ty.deref(), Type::Reference(_)) {
38 errors.push(syn::Error::new_spanned(
39 &t.ty,
40 "dynamic_proxy does not support borrowed arguments; use owned 'static + Send + Sync arguments",
41 ).to_compile_error());
42 }
43 Some(id.clone().ident)
44 },
45 _ => {
46 errors.push(syn::Error::new_spanned(
47 &t.pat,
48 "dynamic_proxy only supports identifier arguments",
49 ).to_compile_error());
50 None
51 }
52 },
53 _ => None
54 }
55 }).collect();
56 let arg_names = args.iter().map(|i| i.to_string());
57 let r = &signature.output;
58 let (return_type, returns_unit) = match r {
59 syn::ReturnType::Type(_, t) => {
60 if matches!(t.deref(), Type::Reference(_)) {
61 errors.push(syn::Error::new_spanned(
62 t,
63 "dynamic_proxy does not support borrowed return values; use owned 'static + Send + Sync return values",
64 ).to_compile_error());
65 }
66 (t.deref().to_token_stream(), false)
67 },
68 _ => (quote!(()), true)
69 };
70 let invocation_stmt: Vec<Stmt> = syn::parse_quote!(
71 let mut invocation_info = ::dynamic_proxy::InvocationInfo {
72 func_name: #func_name,
73 arg_names: &[#(#arg_names),*],
74 arg_values: &[#(::std::boxed::Box::new(#args)),*],
75 return_type: ::std::any::TypeId::of::<#return_type>(),
76 return_value: None
77 };
78 );
79
80 let call_stmt: Vec<Stmt> =
81 match (is_async, returns_unit)
82 {
83 (true, true) => syn::parse_quote!(
84 ::dynamic_proxy::AsyncDynamicProxy::call_async(&self, &mut invocation_info).await;
85 ),
86 (true, false) => syn::parse_quote!(
87 ::dynamic_proxy::AsyncDynamicProxy::call_async(&self, &mut invocation_info).await;
88 return *invocation_info.return_value.unwrap().downcast::<#return_type>().unwrap();
89 ),
90 (false, true) => syn::parse_quote!(
91 ::dynamic_proxy::DynamicProxy::call(&self, &mut invocation_info);
92 ),
93 (false, false) => syn::parse_quote!(
94 ::dynamic_proxy::DynamicProxy::call(&self, &mut invocation_info);
95 return *invocation_info.return_value.unwrap().downcast::<#return_type>().unwrap();
96 )
97 };
98
99 let error_stmt: Vec<Stmt> = errors.into_iter()
100 .map(|error| syn::parse_quote!(#error))
101 .collect();
102 let stmt = [error_stmt.as_slice(), invocation_stmt.as_slice(), call_stmt.as_slice()].concat();
103
104 Some(ImplItem::Fn(ImplItemFn {
105 attrs: func.attrs,
106 vis: Inherited,
107 defaultness: None,
108 sig: signature,
109 block: Block {
110 brace_token: Brace::default(),
111 stmts: stmt,
112 },
113 }))
114 }
115 TraitItem::Const(item) if item.default.is_none() => {
116 Some(syn::parse_quote! {
117 compile_error!("dynamic_proxy does not support required associated constants");
118 })
119 }
120 TraitItem::Type(item) if item.default.is_none() => {
121 Some(syn::parse_quote! {
122 compile_error!("dynamic_proxy does not support required associated types");
123 })
124 }
125 &_ => None
126 }
127 });
128
129 TokenStream::from(quote! {
130 #inp
131 impl #impl_generics #name #ty_generics for #imp #where_clause {
132 #(#body)*
133 }
134 })
135}