Skip to main content

wolfram_export_macros/
lib.rs

1//! Procedural macros for `#[export]`, `#[export_native]`, `#[export_wstp]`,
2//! `#[export_wxf]`, and `#[init]`.
3//!
4//! Emitted paths are resolved dynamically at expansion time via
5//! `proc-macro-crate`: if the caller's `Cargo.toml` has `wolfram-export` the
6//! macro emits `::wolfram_export::*`; if it has `wolfram-library-link` (legacy)
7//! it emits `::wolfram_library_link::*`. Both crates expose the same hidden
8//! runtime surface so generated code resolves correctly in both cases.
9
10mod export;
11
12use proc_macro::TokenStream;
13use proc_macro2::TokenStream as TokenStream2;
14
15use quote::quote;
16use syn::{spanned::Spanned, Error, Item};
17
18//======================================
19// #[init]
20//======================================
21
22/// Mark a function as the library's `WolframLibrary_initialize()` entry point.
23///
24/// The annotated function must take no arguments and return `()`. Behind the
25/// scenes the macro emits a `WolframLibrary_initialize` C symbol that calls
26/// `wolfram_export_native::macro_utils::init_with_user_function(lib, user_fn)`.
27#[proc_macro_attribute]
28pub fn init(attr: TokenStream, item: TokenStream) -> TokenStream {
29    match init_(attr.into(), item) {
30        Ok(tokens) => tokens.into(),
31        Err(err) => err.into_compile_error().into(),
32    }
33}
34
35fn init_(attr: TokenStream2, item: TokenStream) -> Result<TokenStream2, Error> {
36    if !attr.is_empty() {
37        return Err(Error::new(attr.span(), "unexpected attribute arguments"));
38    }
39
40    let item: Item = syn::parse(item)?;
41    let func = match item {
42        Item::Fn(func) => func,
43        _ => {
44            return Err(Error::new(
45                attr.span(),
46                "this attribute can only be applied to `fn(..) {..}` items",
47            ))
48        },
49    };
50
51    if let Some(async_) = func.sig.asyncness {
52        return Err(Error::new(
53            async_.span(),
54            "initialization function cannot be `async`",
55        ));
56    }
57    if let Some(lt) = func.sig.generics.lt_token {
58        return Err(Error::new(
59            lt.span(),
60            "initialization function cannot be generic",
61        ));
62    }
63    if !func.sig.inputs.is_empty() {
64        return Err(Error::new(
65            func.sig.inputs.span(),
66            "initialization function should have zero parameters",
67        ));
68    }
69
70    let user_init_fn_name: syn::Ident = func.sig.ident.clone();
71    let p = &self::export::Prefix::resolve().crate_path;
72
73    Ok(quote! {
74        #func
75
76        #[no_mangle]
77        pub unsafe extern "C" fn WolframLibrary_initialize(
78            lib: #p::sys::WolframLibraryData,
79        ) -> ::std::os::raw::c_int {
80            #p::macro_utils::init_with_user_function(
81                lib,
82                #user_init_fn_name
83            )
84        }
85    })
86}
87
88//======================================
89// #[export] — legacy form, dispatches by args
90//======================================
91
92/// Back-compat `#[export]` / `#[export(wstp)]` proc-macro: dispatches by the
93/// `wstp` keyword in `attrs`. Used by the `wolfram_library_link::export`
94/// re-export so existing call sites compile unchanged — emitted code paths
95/// resolve through `::wolfram_library_link::*`.
96#[proc_macro_attribute]
97pub fn export(attrs: TokenStream, item: TokenStream) -> TokenStream {
98    let attrs: syn::AttributeArgs = syn::parse_macro_input!(attrs);
99    let mode = self::export::detect_mode_from_args(&attrs);
100    let attrs = self::export::strip_wstp_arg(attrs);
101    match self::export::export(mode, attrs, item) {
102        Ok(tokens) => tokens.into(),
103        Err(err) => err.into_compile_error().into(),
104    }
105}
106
107//======================================
108// #[export_native]
109//======================================
110
111/// Annotate a function for export via the native (MArgument-based) Wolfram
112/// LibraryLink ABI. Re-exported by `wolfram-export-native` as `export`.
113#[proc_macro_attribute]
114pub fn export_native(attrs: TokenStream, item: TokenStream) -> TokenStream {
115    let attrs: syn::AttributeArgs = syn::parse_macro_input!(attrs);
116    match self::export::export(self::export::Mode::Native, attrs, item) {
117        Ok(tokens) => tokens.into(),
118        Err(err) => err.into_compile_error().into(),
119    }
120}
121
122//======================================
123// #[export_wstp]
124//======================================
125
126/// Annotate a function for export via the WSTP `LinkObject` ABI. Re-exported
127/// by `wolfram-export-wstp` as `export`.
128#[proc_macro_attribute]
129pub fn export_wstp(attrs: TokenStream, item: TokenStream) -> TokenStream {
130    let attrs: syn::AttributeArgs = syn::parse_macro_input!(attrs);
131    match self::export::export(self::export::Mode::Wstp, attrs, item) {
132        Ok(tokens) => tokens.into(),
133        Err(err) => err.into_compile_error().into(),
134    }
135}
136
137//======================================
138// #[export_wxf]
139//======================================
140
141/// Annotate a function for export via the WXF (typed-arg) ABI. The wrapper
142/// reads a `ByteArray` MArgument containing a WXF-encoded payload,
143/// deserializes via `FromWolfram`, calls the user function, serializes the
144/// return value, and writes a `ByteArray` MArgument back. Re-exported by
145/// `wolfram-export-wxf` as `export`.
146#[proc_macro_attribute]
147pub fn export_wxf(attrs: TokenStream, item: TokenStream) -> TokenStream {
148    let attrs: syn::AttributeArgs = syn::parse_macro_input!(attrs);
149    match self::export::export(self::export::Mode::Wxf, attrs, item) {
150        Ok(tokens) => tokens.into(),
151        Err(err) => err.into_compile_error().into(),
152    }
153}