1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/// Marks a Rust item for FFI export and generates the required interop metadata.
///
/// `#[ffi]` is the unified entry point for annotating structs, enums, functions, constants, and
/// service `impl` blocks. It dispatches to item-specific code generation based on the annotated
/// item kind and accepts a set of optional attributes to control the output.
///
/// # Structs and Enums
///
/// Annotating a struct or enum registers it as an FFI type and derives [`TypeInfo`]. A
/// `#[repr(C)]` is added to structs and `#[repr(u32)]` to enums if no `repr` is present.
///
/// ```rust
/// # use interoptopus::ffi;
/// #[ffi]
/// pub struct Vec2 {
/// pub x: f32,
/// pub y: f32,
/// }
///
/// #[ffi]
/// pub enum Status { Ok, Err }
/// ```
///
/// **Attributes:**
///
/// | Attribute | Description |
/// |-----------|-------------|
/// | `name = "Name"` | Override the name used in generated bindings. |
/// | `module = "name"` / `module = common` | Assign to a named or common module. |
/// | `opaque` | Declare the type opaque (no field layout exposed). |
/// | `packed` | Apply `#[repr(C, packed)]`. |
/// | `transparent` | Apply `#[repr(transparent)]`. |
/// | `debug` | Print the generated code to stderr during compilation. |
///
/// # Functions
///
/// Annotating a `pub fn` registers it as an FFI function and generates a companion struct
/// implementing [`FunctionInfo`]. The function itself is kept as-is.
///
/// ```rust
/// # use interoptopus::ffi;
/// #[ffi]
/// pub fn sum(a: u32, b: u32) -> u32 { a + b }
/// ```
///
/// **Attributes:**
///
/// | Attribute | Description |
/// |-----------|-------------|
/// | `export = "name"` | Override the symbol name used in the generated `.dll` / `.so`. |
/// | `export = unique` | Generate a unique export name to avoid symbol clashes. |
/// | `module = "name"` / `module = common` | Assign to a named or common module. |
/// | `debug` | Print the generated code to stderr during compilation. |
///
/// The generated function uses `extern "C-unwind"` calling convention. If a panic
/// reaches the FFI boundary it will unwind into the caller. What then happens is platform specific.
/// In C# this can surface as a `System.Runtime.InteropServices.SEHException` on Windows, or process
/// aborts in other cases. In other words, you should generally avoid panicking in FFI code through
/// `#[ffi]` functions.
///
/// # Constants
///
/// Annotating a `const` item registers it as an FFI constant and derives [`ConstantInfo`].
///
/// ```rust
/// # use interoptopus::ffi;
/// #[ffi]
/// pub const MAX_ITEMS: u32 = 1024;
/// ```
///
/// **Attributes:**
///
/// | Attribute | Description |
/// |-----------|-------------|
/// | `name = "NAME"` | Override the name used in generated bindings. |
/// | `module = "name"` / `module = common` | Assign to a named or common module. |
/// | `debug` | Print the generated code to stderr during compilation. |
///
/// # Services (impl blocks)
///
/// Annotating an `impl` block turns its public methods into standalone FFI functions and registers
/// the type as a service. The struct itself must also be annotated with `#[ffi]` (no additional
/// attributes required on the `impl` line).
///
/// Methods that should not be exposed can be marked with `#[ffi::skip]`. Non-`pub` methods are
/// automatically excluded.
///
/// ```rust
/// # use interoptopus::ffi;
/// #[ffi]
/// pub enum MyError { General }
///
/// #[ffi(service)]
/// pub struct Counter { count: u32 }
///
/// #[ffi]
/// impl Counter {
/// pub fn create() -> ffi::Result<Self, MyError> { ffi::Ok(Self { count: 0 }) }
/// pub fn increment(&mut self) { self.count += 1; }
/// pub fn get_count(&self) -> u32 { self.count }
/// }
/// ```
///
/// **Attributes:**
///
/// | Attribute | Description |
/// |-----------|-------------|
/// | `prefix = "name"` | Override the `snake_case` prefix used for generated FFI function names. |
/// | `export = unique` | Generate unique export names for all emitted methods to avoid symbol clashes. |
/// | `debug` | Print the generated code to stderr during compilation. |
///
/// # Skipping Fields
///
/// Individual struct fields can be excluded from the FFI layout with `#[ffi::skip]`. This is
/// typically used for marker types like `PhantomData` that carry no runtime data.
///
/// ```rust
/// # use interoptopus::ffi;
/// use std::marker::PhantomData;
///
/// #[ffi]
/// pub struct Tagged {
/// pub id: u32,
/// #[ffi::skip]
/// pub _marker: PhantomData<u32>,
/// }
/// ```
///
/// [`TypeInfo`]: crate::lang::rust::TypeInfo
/// [`FunctionInfo`]: crate::lang::rust::FunctionInfo
/// [`ConstantInfo`]: crate::lang::rust::ConstantInfo
pub use ffi;
/// Derives the [`AsyncRuntime`](crate::pattern::asynk::AsyncRuntime) trait for a service struct
/// by forwarding to one of its fields.
///
/// The macro looks for the runtime field in this order:
/// 1. A field annotated with `#[runtime]`.
/// 2. A field named `runtime`.
///
/// The chosen field's type must itself implement [`AsyncRuntime`](crate::pattern::asynk::AsyncRuntime). The generated impl
/// delegates [`spawn`](crate::pattern::asynk::AsyncRuntime::spawn) to that field.
///
/// # Example
///
/// ```rust
/// use interoptopus::{AsyncRuntime, ffi};
/// use interoptopus::rt::Tokio;
///
/// #[ffi(service)]
/// #[derive(AsyncRuntime)]
/// pub struct MyService {
/// runtime: Tokio,
/// }
/// ```
///
/// When the field is not named `runtime`, mark it with `#[runtime]`:
///
/// ```rust
/// use interoptopus::{AsyncRuntime, ffi};
/// use interoptopus::rt::Tokio;
///
/// #[ffi(service)]
/// #[derive(AsyncRuntime)]
/// pub struct MyService {
/// #[runtime]
/// rt: Tokio,
/// }
/// ```
pub use AsyncRuntime;
/// Declares a plugin interface for reverse interop, e.g., loading a C# DLL from Rust.
///
/// Whereas normal interoptopus use exports Rust code to other languages, `plugin!` works in the
/// opposite direction: it lets Rust *call into* a foreign library (e.g. a C# `.dll`) by generating
/// a typed Rust struct whose methods dispatch through FFI function pointers loaded at runtime.
///
/// # Syntax
///
/// Plugins are defined with a special syntax that can declare direct functions and instantiatable
/// services:
///
/// ```rust
/// use interoptopus::ffi;
/// use interoptopus::lang::meta::Visibility::Public;
/// use interoptopus::lang::types::TypeKind::Enum;
/// use interoptopus::wire::Wire;
///
/// # #[ffi]
/// # #[derive(Clone)]
/// # enum Error { A }
/// #
/// interoptopus::plugin!(MyPlugin {
/// // Direct synchronous and async functions
/// fn add_one(x: u32) -> u32;
/// async fn process(data: Wire<String>);
///
/// // Service blocks: a constructor returning Self plus instance methods
/// impl Processor {
/// fn create(name: Wire<String>) -> Self;
/// fn run(&self, x: f32) -> f32;
/// async fn run_async(&self) -> ffi::Result<u8, Error>;
/// }
/// });
/// ```
///
/// For the example above, the macro generates a `MyPlugin` struct with the following methods:
///
/// ```rust,ignore
/// // Direct functions — call straight through to the loaded FFI symbol:
/// plugin.add_one(1)
/// plugin.process(Wire::from("hi")).await
///
/// // Service constructor — symbol name is the lowercased type name + "_create":
/// let proc: Processor = plugin.processor_create(Wire::from("my-proc"));
///
/// // Instance methods on the returned Processor value:
/// proc.run(1.5)
/// proc.run_async().await
/// ```
///
/// # Loading a plugin
///
/// To instantiate a plugin a backend-specific loader is needed. For example,
/// the `interoptopus_csharp` crate provides both a `DotnetRuntime` and an `AotRuntime` for
/// loading `.NET` assemblies:
///
/// ```rust,ignore
/// let plugin = aot::runtime()?
/// .exception_handler(|msg| eprintln!("plugin error: {msg}"))
/// .load::<MyPlugin>("path/to/my_plugin.dll")?;
/// ```
///
/// Note, this example is illustrative, the actual API is subject to change.
///
/// # Supported types
///
/// Not every Rust type can appear in every position. The table below summarises what
/// works where. Positions are: **Arg** (function/method parameter), **Ret** (return
/// type), **Field** (inside an `#[ffi]` struct/enum passed across the boundary), and
/// **Async** (usable in `async fn` signatures).
///
/// | Type | Arg | Ret | Field | Async | Notes |
/// |------|:---:|:---:|:-----:|:-----:|-------|
/// | `u8`, `u16`, `u32`, ... | ✅ | ✅ | ✅ | ✅ | Always work everywhere. |
/// | `[T; N]` | ❌ | ❌ | ✅ | ❌ | Arrays only supported in fields. |
/// | `#[ffi] struct MyStruct { .. }` | ✅ | ✅ | ✅ | ✅ | All fields must themselves be FFI-safe. |
/// | `#[ffi] enum MyEnum { .. }` | ✅ | ✅ | ✅ | ✅ | Same as structs. |
/// | `String`, `Vec<T>`, `HashMap<K, V>` | ✅ | ✅ | ✅ | ✅ | Only if self or parent within `Wire<T>`. |
/// | `&T` | ✅ | ❌ | ❌ | ❌️ | Only supported for services for now. |
/// | `&mut T` | ❌ | ❌ | ❌ | ❌️ | For now unsupported. |
/// | `MyService` (an `impl` block) | ✅ | ✅️ | ❌ | ✅ | Cannot be embedded in fields. |
/// | [`Wire<T>`](crate::wire::Wire) | ✅ | ✅ | ❌️ | ✅ | Can only be used on functions, not in fields. |
/// | [`ffi::Result<T, E>`](crate::pattern::result::Result) | ✅ | ✅ | ✅ | ✅ | Same as enum w.r.t, `T`, `E`. |
/// | [`ffi::Option<T>`](crate::pattern::option::Option) | ✅ | ✅ | ✅ | ✅ | Same as enum w.r.t. `T`. |
/// | `Try<T>` (from `interoptopus_csharp`) | ❌ | ✅ | ❌ | ✅ | Magic C# exception converter. |
///
/// This list is not exhaustive and there might be subtleties involved.
/// However, there should be a compile error if you accidentally try to use a type that is not supported.
/// If not, please file a bug.
///
/// # Design guidelines
///
/// Think of your plugin API as a nanosecond-latency **web server**: the FFI boundary
/// is a network boundary with benefits, and most types have pass-by-value semantics.
///
/// While simple data can have sub-nanosecond cost passing through it (essentially living in registers or
/// a simple stack push), these serialization semantics are still the prevalent model to
/// reason about your API. Extras like callbacks and pointers exist, but FFI and ownership
/// constraints prevent them from
/// nesting arbitrarily.
///
/// Some things to keep in mind:
/// - While you can load multiple plugins, each process can only ever have one instance of
/// a plugin.
/// - Plugins usually cannot be unloaded.
/// - Plugin methods therefore act like singletons / statics.
/// - You can have services via `impl Service {}` blocks. They can be arbitrarily created
/// and destroyed. However, due to the aforementioned pass-by-value semantics they cannot
/// be arbitrarily nested inside other data structures. The only way to pass them around
/// is via function arguments, which takes care of their lifecycle requirements.
pub use plugin;
/// Strips module paths from a fully-qualified Rust type name, preserving generic structure.
///
/// For example, `"my_crate::module::Struct<alloc::string::String>"` becomes `"Struct<String>"`.
/// Handles nested generics and multiple type parameters.