lib.rs

 1use core::panic;
 2
 3use proc_macro::TokenStream;
 4use quote::{format_ident, quote};
 5use syn::{parse_macro_input, ItemFn, VisPublic, 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    TokenStream::from(quote! {
23        use serde;
24
25        #[no_mangle]
26        #inner_fn
27
28        #[no_mangle]
29        pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const Buffer {
30            // setup
31            let buffer = Buffer { ptr, len };
32            let data = unsafe { buffer.to_vec() };
33
34            // operation
35            let argument = bincode::deserialize(&data).unwrap();
36            let result = #inner_fn_name(argument);
37            let new_data: Result<Vec<u8>, _> = bincode::serialize(&result);
38            let new_data = new_data.unwrap();
39
40            // teardown
41            let new_buffer = unsafe { Buffer::from_vec(new_data) };
42            return new_buffer.leak_to_heap();
43        }
44    })
45}