Skip to main content

dora_operator_api_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4
5extern crate proc_macro;
6
7#[proc_macro]
8pub fn register_operator(item: TokenStream) -> TokenStream {
9    // convert from `TokenStream` to `TokenStream2`, which is used by the
10    // `syn` crate
11    let item = TokenStream2::from(item);
12    // generate the dora wrapper functions
13    let generated = register_operator_impl(&item).unwrap_or_else(|err| err.to_compile_error());
14    // output the generated functions
15    let tokens = quote! {
16        #generated
17    };
18    // convert the type back from `TokenStream2` to `TokenStream`
19    tokens.into()
20}
21
22/// Generates the wrapper functions for the annotated function.
23fn register_operator_impl(item: &TokenStream2) -> syn::Result<TokenStream2> {
24    // parse the type given to the `register_operator` macro
25    let operator_ty: syn::TypePath = syn::parse2(item.clone())
26        .map_err(|e| syn::Error::new(e.span(), "expected type as argument"))?;
27
28    let init = quote! {
29        #[unsafe(no_mangle)]
30        pub unsafe extern "C" fn dora_init_operator() -> dora_operator_api::types::DoraInitResult {
31            dora_operator_api::raw::dora_init_operator::<#operator_ty>()
32        }
33
34        const _DORA_INIT_OPERATOR: dora_operator_api::types::DoraInitOperator = dora_operator_api::types::DoraInitOperator {
35            init_operator: dora_init_operator,
36        };
37    };
38
39    let drop = quote! {
40        #[unsafe(no_mangle)]
41        pub unsafe extern "C" fn dora_drop_operator(operator_context: *mut std::ffi::c_void)
42            -> dora_operator_api::types::DoraResult
43        {
44            dora_operator_api::raw::dora_drop_operator::<#operator_ty>(operator_context)
45        }
46
47        const _DORA_DROP_OPERATOR: dora_operator_api::types::DoraDropOperator = dora_operator_api::types::DoraDropOperator {
48            drop_operator: dora_drop_operator,
49        };
50    };
51
52    let on_event = quote! {
53        #[unsafe(no_mangle)]
54        pub unsafe extern "C" fn dora_on_event(
55            event: &mut dora_operator_api::types::RawEvent,
56            send_output: &dora_operator_api::types::SendOutput,
57            operator_context: *mut std::ffi::c_void,
58        ) -> dora_operator_api::types::OnEventResult {
59            dora_operator_api::raw::dora_on_event::<#operator_ty>(
60                event, send_output, operator_context
61            )
62        }
63
64        const _DORA_ON_EVENT: dora_operator_api::types::DoraOnEvent = dora_operator_api::types::DoraOnEvent {
65            on_event: dora_operator_api::types::OnEventFn(dora_on_event),
66        };
67    };
68
69    // On Windows, we need to explicitly export symbols when linking a Rust staticlib
70    // into a C++ DLL. This embeds linker directives directly into the object file.
71    // SAFETY: The `.drectve` section is specifically designed for passing linker
72    // directives on Windows. The content is a valid null-terminated string of
73    // MSVC linker options that only affects symbol visibility.
74    let windows_exports = quote! {
75        #[cfg(target_os = "windows")]
76        #[unsafe(link_section = ".drectve")]
77        #[used]
78        static _DORA_EXPORTS: [u8; 77] =
79            *b" /EXPORT:dora_init_operator /EXPORT:dora_drop_operator /EXPORT:dora_on_event ";
80    };
81
82    Ok(quote! {
83        #init
84        #drop
85        #on_event
86        #windows_exports
87    })
88}