1use core::panic;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{parse_macro_input, ItemFn, Visibility};
6
7#[proc_macro_attribute]
8pub fn bind(args: TokenStream, function: TokenStream) -> TokenStream {
9 if !args.is_empty() {
10 panic!("The bind attribute does not take any arguments");
11 }
12
13 let inner_fn = parse_macro_input!(function as ItemFn);
14 if let Visibility::Public(_) = inner_fn.vis {
15 } else {
16 panic!("The bind attribute only works for public functions");
17 }
18
19 let inner_fn_name = format_ident!("{}", inner_fn.sig.ident);
20 let outer_fn_name = format_ident!("__{}", inner_fn_name);
21
22 let variadic = inner_fn.sig.inputs.len();
23 let mut args = vec![];
24 for i in 0..variadic {
25 let ident = format_ident!("__{}_arg_{}", inner_fn_name, i);
26 args.push(ident);
27 }
28
29 let args = quote! {
30 ( #(args ,) )*
31 };
32
33 TokenStream::from(quote! {
34 #[no_mangle]
35 #inner_fn
36
37 #[no_mangle]
38 pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer {
39 // setup
40 let buffer = ::plugin::__Buffer { ptr, len };
41 let data = unsafe { buffer.to_vec() };
42
43 // operation
44 let #args = ::plugin::bincode::deserialize(&data).unwrap();
45 let result = #inner_fn_name #args;
46 let new_data: Result<Vec<u8>, _> = ::plugin::bincode::serialize(&result);
47 let new_data = new_data.unwrap();
48
49 // teardown
50 let new_buffer = unsafe { ::plugin::__Buffer::from_vec(new_data) };
51 return new_buffer.leak_to_heap();
52 }
53 })
54}