fidius_macro/lib.rs
1// Copyright 2026 Colliery, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod impl_macro;
16mod interface;
17mod ir;
18mod wit;
19
20use proc_macro::TokenStream;
21use syn::{parse_macro_input, ItemImpl, ItemTrait};
22
23use impl_macro::PluginImplAttrs;
24use ir::InterfaceAttrs;
25
26/// Define a plugin interface from a trait.
27///
28/// Generates a `#[repr(C)]` vtable struct, interface hash constant,
29/// capability bit constants, and a descriptor builder function.
30///
31/// # Example
32///
33/// ```ignore
34/// #[plugin_interface(version = 1, buffer = PluginAllocated)]
35/// pub trait Greeter: Send + Sync {
36/// fn greet(&self, name: String) -> String;
37///
38/// #[optional(since = 2)]
39/// fn greet_fancy(&self, name: String) -> String;
40/// }
41/// ```
42#[proc_macro_attribute]
43pub fn plugin_interface(attr: TokenStream, item: TokenStream) -> TokenStream {
44 let attrs = parse_macro_input!(attr as InterfaceAttrs);
45 let item_trait = parse_macro_input!(item as ItemTrait);
46
47 match ir::parse_interface(attrs, &item_trait) {
48 Ok(ir) => match interface::generate_interface(&ir) {
49 Ok(tokens) => tokens.into(),
50 Err(err) => err.to_compile_error().into(),
51 },
52 Err(err) => err.to_compile_error().into(),
53 }
54}
55
56/// Implement a plugin interface for a concrete type.
57///
58/// Generates extern "C" FFI shims, a static vtable, a plugin descriptor,
59/// and a plugin registry.
60///
61/// # Example
62///
63/// ```ignore
64/// pub struct MyGreeter;
65///
66/// #[plugin_impl(Greeter)]
67/// impl Greeter for MyGreeter {
68/// fn greet(&self, name: String) -> String {
69/// format!("Hello, {name}!")
70/// }
71/// }
72/// ```
73#[proc_macro_attribute]
74pub fn plugin_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
75 let attrs = parse_macro_input!(attr as PluginImplAttrs);
76 let item_impl = parse_macro_input!(item as ItemImpl);
77
78 match impl_macro::generate_plugin_impl(&attrs, &item_impl) {
79 Ok(tokens) => tokens.into(),
80 Err(err) => err.to_compile_error().into(),
81 }
82}
83
84/// Mark a `struct`/`enum` as usable in a WASM plugin interface (FIDIUS-I-0023).
85///
86/// This is a **marker** derive: it emits no code. The `fidius wit` generator
87/// (run from `build.rs`) keys on the `#[derive(WitType)]` attribute when it
88/// parses the crate source, mapping the struct to a WIT `record` (named fields)
89/// or the enum to a WIT `variant` (unit / single-field cases) and emitting the
90/// generated↔author conversions the wasm adapter uses. The same type continues
91/// to cross the cdylib/Python boundary via serde, unchanged.
92///
93/// ```ignore
94/// #[derive(WitType, serde::Serialize, serde::Deserialize, Clone)]
95/// pub struct Point { pub x: i32, pub y: i32 }
96/// ```
97#[proc_macro_derive(WitType)]
98pub fn derive_wit_type(_item: TokenStream) -> TokenStream {
99 // Intentionally empty — the build-time WIT generator reads the annotation
100 // from source; no per-type codegen is needed here.
101 TokenStream::new()
102}