Skip to main content

weaveffi_macros/
lib.rs

1//! Procedural macros that turn safe, annotated Rust into the WeaveFFI C ABI.
2//!
3//! A producer annotates an ordinary Rust module with `#[weaveffi::module]` and
4//! tags the items it wants to export. The macro lowers the module to the
5//! WeaveFFI IR (through [`weaveffi_bridge`]), builds the canonical
6//! [`BindingModel`](weaveffi_core::model::BindingModel), and emits the
7//! `#[no_mangle] extern "C"` thunks every generated language binding calls.
8//! All of the `unsafe` marshalling lives in the `weaveffi-abi` runtime, so the
9//! producer writes only safe Rust.
10//!
11//! ```ignore
12//! #[weaveffi::module]
13//! pub mod calculator {
14//!     /// Add two integers.
15//!     #[weaveffi::export]
16//!     pub fn add(a: i32, b: i32) -> i32 {
17//!         a + b
18//!     }
19//! }
20//!
21//! weaveffi::export_runtime!();
22//! ```
23//!
24//! The same IR the macro lowers is what `weaveffi generate path/to/lib.rs`
25//! reads, so the generated bindings and the producer cannot drift.
26//!
27//! # Attributes
28//!
29//! * [`macro@module`] marks an exported namespace (the driver attribute).
30//! * [`macro@export`] exports a function; [`macro@record`] a by-value struct;
31//!   [`macro@enumeration`] a `#[repr(i32)]` C-style enum.
32//! * [`macro@interface`] declares an opaque object type whose `impl` block's
33//!   `pub fn`s become constructors, methods, and statics.
34//! * [`macro@error`] declares the module's error domain from a unit-variant
35//!   enum with explicit discriminants.
36//! * [`macro@callback`] / [`macro@listener`] declare a callback and an event
37//!   listener; [`macro@cancellable`] marks an async function as cancellable.
38//!
39//! The item-level attributes are inert markers that [`macro@module`] reads; on
40//! their own they expand to the item unchanged.
41
42#![deny(missing_docs)]
43
44use proc_macro::TokenStream;
45
46mod codegen;
47
48/// Mark an inline `mod` as an exported WeaveFFI namespace.
49///
50/// The macro re-emits the module unchanged and appends the generated C ABI
51/// thunks for every tagged item it contains (functions, records, enums). Apply
52/// it to a `mod foo { ... }` whose items carry the item-level markers.
53#[proc_macro_attribute]
54pub fn module(_attr: TokenStream, item: TokenStream) -> TokenStream {
55    let item_mod = syn::parse_macro_input!(item as syn::ItemMod);
56    codegen::expand_module(&item_mod)
57        .unwrap_or_else(syn::Error::into_compile_error)
58        .into()
59}
60
61/// Generate `#[doc(hidden)]` no-op marker attributes that [`macro@module`]
62/// reads. Each expands to the annotated item unchanged.
63macro_rules! marker_attr {
64    ($(#[$meta:meta])* $name:ident) => {
65        $(#[$meta])*
66        #[proc_macro_attribute]
67        pub fn $name(_attr: TokenStream, item: TokenStream) -> TokenStream {
68            item
69        }
70    };
71}
72
73marker_attr! {
74    /// Export a function across the FFI boundary. An `async fn` lowers to an
75    /// asynchronous symbol; a `fn -> Result<T, E>` is fallible.
76    export
77}
78marker_attr! {
79    /// Declare a by-value record (struct) with generated create/getters.
80    record
81}
82marker_attr! {
83    /// Declare an interface: an opaque object type with constructors, methods,
84    /// and statics read from its `impl` block. Methods must take `&self`.
85    interface
86}
87marker_attr! {
88    /// Declare the module's error domain from a unit-variant enum with
89    /// explicit discriminants. The module macro generates the matching
90    /// `ErrorReport` implementation.
91    error
92}
93marker_attr! {
94    /// Declare a C-style `#[repr(i32)]` enum exported by value.
95    enumeration
96}
97marker_attr! {
98    /// Declare a callback function signature the host implements.
99    callback
100}
101marker_attr! {
102    /// Declare an event listener; takes `event = "CallbackName"`.
103    listener
104}
105marker_attr! {
106    /// Mark an async function as accepting a cancellation token.
107    cancellable
108}
109marker_attr! {
110    /// Opt a record into a generated fluent builder.
111    builder
112}