1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use proc_macro::TokenStream;
use proc_macro_crate::{crate_name, FoundCrate};
use quote::{quote, TokenStreamExt};
use syn::{
    parse_macro_input, parse_str, AttributeArgs, FnArg, ForeignItem, Ident, ItemFn, ItemForeignMod,
    Lit, NestedMeta,
};

#[proc_macro_attribute]
pub fn export(_attr: TokenStream, input: TokenStream) -> TokenStream {
    let func = input.clone();
    let func = parse_macro_input!(func as ItemFn);
    let name = func.sig.ident;
    let name_str = name.to_string();
    let expname = parse_str::<Ident>(&format!("__export_{}", name_str)).unwrap();
    let input = proc_macro2::TokenStream::from(input);
    let export_func = quote! {
        #[doc(hidden)]
        #[export_name = #name_str]
        extern "C" fn #expname(len: usize, data: *const u8) -> u64 {
            ::ayaka_bindings::__export(len, data, #name)
        }
        #input
    };
    TokenStream::from(export_func)
}

#[proc_macro_attribute]
pub fn import(attr: TokenStream, input: TokenStream) -> TokenStream {
    let attr = parse_macro_input!(attr as AttributeArgs);
    let mut module = String::new();
    for a in attr {
        match a {
            NestedMeta::Lit(Lit::Str(s)) => module = s.value(),
            _ => unimplemented!(),
        }
    }
    let input = parse_macro_input!(input as ItemForeignMod);
    let mut imports = quote! {};
    for func in input.items {
        match func {
            ForeignItem::Fn(func) => {
                let attrs = func.attrs;
                let vis = func.vis;
                let sig = func.sig;

                let params = sig.inputs.clone();
                let params = params
                    .into_iter()
                    .map(|arg| match arg {
                        FnArg::Typed(p) => p.pat,
                        _ => unimplemented!(),
                    })
                    .collect::<Vec<_>>();

                let name = sig.ident.clone();
                let name_str = name.to_string();
                let impname = parse_str::<Ident>(&format!("__import_{}", name_str)).unwrap();
                let bindings_crate_name = match crate_name("ayaka-bindings").unwrap() {
                    FoundCrate::Itself => quote!(crate),
                    FoundCrate::Name(name) => {
                        let name = parse_str::<Ident>(&name).unwrap();
                        quote!(::#name)
                    }
                };
                let import_func = quote! {
                    #[doc(hidden)]
                    #[link(wasm_import_module = #module)]
                    extern "C" {
                        #[link_name = #name_str]
                        fn #impname(len: usize, data: *const u8) -> u64;
                    }
                    #(#attrs)* #vis #sig {
                        #bindings_crate_name::__import(#impname, (#(#params,)*))
                    }
                };
                imports.append_all(import_func);
            }
            _ => unimplemented!(),
        }
    }
    TokenStream::from(imports)
}