Skip to main content

player_plugin_macros/
lib.rs

1//! Procedural macros for the safe Vesper plugin author SDK.
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{ItemFn, parse_macro_input, spanned::Spanned};
6
7/// Exports a zero-argument plugin factory through the stable native entry.
8#[proc_macro_attribute]
9pub fn export(args: TokenStream, item: TokenStream) -> TokenStream {
10    if !args.is_empty() {
11        return syn::Error::new(
12            proc_macro2::Span::call_site(),
13            "`export` takes no arguments",
14        )
15        .to_compile_error()
16        .into();
17    }
18
19    let factory = parse_macro_input!(item as ItemFn);
20    let signature = &factory.sig;
21    if signature.asyncness.is_some() {
22        return syn::Error::new(signature.span(), "plugin factories cannot be async")
23            .to_compile_error()
24            .into();
25    }
26    if !signature.generics.params.is_empty() || signature.generics.where_clause.is_some() {
27        return syn::Error::new(
28            signature.generics.span(),
29            "plugin factories cannot be generic",
30        )
31        .to_compile_error()
32        .into();
33    }
34    if !signature.inputs.is_empty() {
35        return syn::Error::new(
36            signature.inputs.span(),
37            "plugin factories must take no arguments",
38        )
39        .to_compile_error()
40        .into();
41    }
42
43    let factory_name = &signature.ident;
44    quote! {
45        #factory
46
47        #[doc(hidden)]
48        #[allow(unsafe_code)]
49        #[unsafe(no_mangle)]
50        pub extern "C" fn vesper_plugin_entry()
51            -> *const ::player_plugin::__private::VesperPluginRoot
52        {
53            ::player_plugin::__private::export_plugin(#factory_name)
54        }
55    }
56    .into()
57}