wolfram_library_link_macros/
lib.rs

1mod export;
2
3
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6
7use quote::quote;
8use syn::{spanned::Spanned, Error, Item};
9
10//======================================
11// #[wolfram_library_link::init]
12//======================================
13
14#[proc_macro_attribute]
15pub fn init(
16    attr: proc_macro::TokenStream,
17    item: proc_macro::TokenStream,
18) -> proc_macro::TokenStream {
19    match init_(attr.into(), item) {
20        Ok(tokens) => tokens.into(),
21        Err(err) => err.into_compile_error().into(),
22    }
23}
24
25fn init_(attr: TokenStream2, item: TokenStream) -> Result<TokenStream2, Error> {
26    // Validate that we got `#[init]` and not `#[init(some, unexpected, arguments)]`.
27    if !attr.is_empty() {
28        return Err(Error::new(attr.span(), "unexpected attribute arguments"));
29    }
30
31    //--------------------------------------------------------------------
32    // Validate that this attribute was applied to a `fn(..) { .. }` item.
33    //--------------------------------------------------------------------
34
35    let item: Item = syn::parse(item)?;
36
37    let func = match item {
38        Item::Fn(func) => func,
39        _ => {
40            return Err(Error::new(
41                attr.span(),
42                "this attribute can only be applied to `fn(..) {..}` items",
43            ))
44        },
45    };
46
47    //-------------------------
48    // Validate the user `func`
49    //-------------------------
50
51    // No `async`
52    if let Some(async_) = func.sig.asyncness {
53        return Err(Error::new(
54            async_.span(),
55            "initialization function cannot be `async`",
56        ));
57    }
58
59    // No generics
60    if let Some(lt) = func.sig.generics.lt_token {
61        return Err(Error::new(
62            lt.span(),
63            "initialization function cannot be generic",
64        ));
65    }
66
67    // No parameters
68    if !func.sig.inputs.is_empty() {
69        return Err(Error::new(
70            func.sig.inputs.span(),
71            "initialization function should have zero parameters",
72        ));
73    }
74
75    //--------------------------------------------------------
76    // Create the output WolframLibrary_initialize() function.
77    //--------------------------------------------------------
78
79    let user_init_fn_name: syn::Ident = func.sig.ident.clone();
80
81    let output = quote! {
82        #func
83
84        #[no_mangle]
85        pub unsafe extern "C" fn WolframLibrary_initialize(
86            lib: ::wolfram_library_link::sys::WolframLibraryData,
87        ) -> ::std::os::raw::c_int {
88            ::wolfram_library_link::macro_utils::init_with_user_function(
89                lib,
90                #user_init_fn_name
91            )
92        }
93    };
94
95    Ok(output)
96}
97
98//======================================
99// #[wolfram_library_link::export]
100//======================================
101
102#[proc_macro_attribute]
103pub fn export(
104    attrs: proc_macro::TokenStream,
105    item: proc_macro::TokenStream,
106) -> proc_macro::TokenStream {
107    let attrs: syn::AttributeArgs = syn::parse_macro_input!(attrs);
108
109    match self::export::export(attrs, item) {
110        Ok(tokens) => tokens.into(),
111        Err(err) => err.into_compile_error().into(),
112    }
113}