1use core::panic;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{parse_macro_input, FnArg, ItemFn, Type, 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 i = (0..variadic).map(syn::Index::from);
24 let t: Vec<Type> = inner_fn
25 .sig
26 .inputs
27 .iter()
28 .map(|x| match x {
29 FnArg::Receiver(_) => {
30 panic!("all arguments must have specified types, no `self` allowed")
31 }
32 FnArg::Typed(item) => *item.ty.clone(),
33 })
34 .collect();
35
36 // this is cursed...
37 let (args, ty) = if variadic != 1 {
38 (
39 quote! {
40 #( data.#i ),*
41 },
42 quote! {
43 ( #( #t ),* )
44 },
45 )
46 } else {
47 let ty = &t[0];
48 (quote! { data }, quote! { #ty })
49 };
50
51 TokenStream::from(quote! {
52 #[no_mangle]
53 #inner_fn
54
55 #[no_mangle]
56 pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer {
57 // setup
58 let buffer = ::plugin::__Buffer { ptr, len };
59 let data = unsafe { buffer.to_vec() };
60
61 // operation
62 let data: #ty = match ::plugin::bincode::deserialize(&data) {
63 Ok(d) => d,
64 Err(e) => panic!("Data passed to function not deserializable."),
65 };
66 let result = #inner_fn_name(#args);
67 let new_data: Result<Vec<u8>, _> = ::plugin::bincode::serialize(&result);
68 let new_data = new_data.unwrap();
69
70 // teardown
71 let new_buffer = unsafe { ::plugin::__Buffer::from_vec(new_data) };
72 return new_buffer.leak_to_heap();
73 }
74 })
75}