Skip to main content

miniextendr_macros/
typed_external_macro.rs

1//! `impl_typed_external!` — explicit monomorphization of `TypedExternal` for generic types.
2//!
3//! Since `#[derive(ExternalPtr)]` rejects generic types (the generated .Call entrypoints
4//! and R wrapper code cannot be generic), this macro provides an alternative for users
5//! who want to use `ExternalPtr<T>` with a specific monomorphization of a generic type.
6//!
7//! # Usage
8//!
9//! ```ignore
10//! impl_typed_external!(MyWrapper<i32>);
11//! impl_typed_external!(MyWrapper<String>);
12//! impl_typed_external!(TreeNode<String, Vec<u8>>);
13//! ```
14//!
15//! # Generated Code
16//!
17//! For `impl_typed_external!(MyWrapper<i32>)`:
18//!
19//! ```ignore
20//! impl TypedExternal for MyWrapper<i32> {
21//!     const TYPE_NAME: &'static str = "MyWrapper<i32>";
22//!     const TYPE_NAME_CSTR: &'static [u8] = b"MyWrapper<i32>\0";
23//!     const TYPE_ID_CSTR: &'static [u8] =
24//!         concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION"),
25//!                 "::", module_path!(), "::MyWrapper<i32>\0").as_bytes();
26//! }
27//!
28//! impl IntoExternalPtr for MyWrapper<i32> {}
29//! ```
30
31use proc_macro2::TokenStream;
32
33/// Implementation of `impl_typed_external!`.
34///
35/// Accepts a concrete type path with generic arguments filled in
36/// (e.g., `MyWrapper<i32>`, `TreeNode<String, Vec<u8>>`).
37///
38/// Returns a `TokenStream` containing:
39/// - `impl TypedExternal for <type> { ... }`
40/// - `impl IntoExternalPtr for <type> {}`
41pub fn impl_typed_external(input: TokenStream) -> syn::Result<TokenStream> {
42    // Parse the input as a Type (supports paths with generic args)
43    let ty: syn::Type = syn::parse2(input)?;
44
45    // Extract a display name for the type. We use the token representation
46    // which naturally includes angle brackets and generic args.
47    let type_display = quote::quote!(#ty).to_string();
48    // Clean up spacing: `MyWrapper < i32 >` → `MyWrapper<i32>`
49    let type_display = type_display
50        .replace(" < ", "<")
51        .replace(" > ", ">")
52        .replace("< ", "<")
53        .replace(" >", ">");
54
55    let name_lit = syn::LitStr::new(&type_display, proc_macro2::Span::call_site());
56    let name_cstr_bytes = format!("{}\0", type_display);
57    let name_cstr =
58        syn::LitByteStr::new(name_cstr_bytes.as_bytes(), proc_macro2::Span::call_site());
59
60    Ok(quote::quote! {
61        impl ::miniextendr_api::externalptr::TypedExternal for #ty {
62            const TYPE_NAME: &'static str = #name_lit;
63            const TYPE_NAME_CSTR: &'static [u8] = #name_cstr;
64            const TYPE_ID_CSTR: &'static [u8] =
65                concat!(
66                    env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION"),
67                    "::", module_path!(), "::", #name_lit, "\0"
68                ).as_bytes();
69        }
70
71        impl ::miniextendr_api::externalptr::IntoExternalPtr for #ty {}
72    })
73}