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}