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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/// 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 }
/// ```
///
/// <br>
///
/// The following field types are supported in `#[ffi]` structs and enums:
///
/// | Type | Field | Notes |
/// |------|:-----:|-------|
/// | `u8`, `u16`, `u32`, ... | โ
| Always supported. |
/// | `[T; N]` | โ
| Fixed-size arrays. |
/// | `#[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 is within `Wire<T>`. |
/// | `Option<T>` | โ
| As above, or if `T` is reference. |
/// | `&T` / `&mut T` | โ
| For `T` same a structs. |
/// | `MyService` | โ | Cannot be embedded in fields. |
/// | [`Wire<T>`](crate::wire::Wire) | โ | Only usable 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`. |
/// | [`ffi::Vec<T>`](crate::pattern::vec::Vec), [`ffi::String`](crate::pattern::string::String) | โ
| Same as struct w.r.t. `T`. |
/// | [`ffi::Slice<T>`](crate::pattern::slice::Slice), [`ffi::SliceMut<T>`](crate::pattern::slice::SliceMut) | โ
| Same as struct w.r.t. `T`. |
/// | [`ffi::CStrPtr`](crate::pattern::cstr::CStrPtr) | โ | Not supported by codegen. |
///
/// <br>
///
/// In addition, the macro accepts the following options:
///
/// | 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 }
/// ```
///
/// <br>
///
/// The following types are supported in `#[ffi]` function signatures:
///
/// | Type | Arg | Ret | Async | Notes |
/// |------|:---:|:---:|:-----:|-------|
/// | `u8`, `u16`, `u32`, ... | โ
| โ
| โ
| Always supported. |
/// | `[T; N]` | โ | โ | โ | Arrays only supported in struct fields. |
/// | `#[ffi] struct MyStruct { .. }` | โ
| โ
| โ
| All fields must be FFI-safe. |
/// | `#[ffi] enum MyEnum { .. }` | โ
| โ
| โ
| Same as structs. |
/// | `String`, `Vec<T>`, `HashMap<K, V>` | โ
| โ
| โ
| Only within `Wire<T>`. |
/// | `Option<T>` | โ
| โ
| โ
| As above, or if `T` is reference. |
/// | `&T` / `&mut T` | โ
| โ
| โ | Caller must uphold `&mut` guarantee! |
/// | `MyService` | โ
| โ | โ | May only appear as `&MyService`. |
/// | [`Wire<T>`](crate::wire::Wire) | โ
| โ
| โ
| Serialized transfer. |
/// | [`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`. |
/// | [`ffi::Vec<T>`](crate::pattern::vec::Vec), [`ffi::String`](crate::pattern::string::String) | โ
| โ
| โ
| Same as struct w.r.t. `T`. |
/// | [`ffi::Slice<T>`](crate::pattern::slice::Slice), [`ffi::SliceMut<T>`](crate::pattern::slice::SliceMut) | โ
| โ
| โ
| Same as struct w.r.t. `T`. |
/// | [`ffi::CStrPtr`](crate::pattern::cstr::CStrPtr) | โ
| โ
| โ | Async can't accept pointers. |
///
/// <br>
///
/// In addition, the macro accepts the following options:
///
/// | 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;
/// ```
///
/// <br>
///
/// The following types are supported as `#[ffi]` constant values:
///
/// | Type | Value | Notes |
/// |------|:-----:|-------|
/// | `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64` | โ
| Integer literals. |
/// | `f32`, `f64` | โ
| Floating-point literals. |
/// | `bool` | โ
| Boolean literals. |
/// | Everything else | โ | Only primitive scalar types are supported. |
///
/// <br>
///
/// In addition, the macro accepts the following options:
///
/// | 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 }
/// }
/// ```
///
/// <br>
///
/// The following types are supported in `#[ffi]` service method signatures:
///
/// | Type | Arg | Ret | Async | Notes |
/// |------|:---:|:---:|:-----:|-------|
/// | `u8`, `u16`, `u32`, ... | โ
| โ
| โ
| Always supported. |
/// | `[T; N]` | โ | โ | โ | Arrays only supported in struct fields. |
/// | `#[ffi] struct MyStruct { .. }` | โ
| โ
| โ
| All fields must be FFI-safe. |
/// | `#[ffi] enum MyEnum { .. }` | โ
| โ
| โ
| Same as structs. |
/// | `String`, `Vec<T>`, `HashMap<K, V>` | โ
| โ
| โ
| Only within `Wire<T>`. |
/// | `Option<T>` | โ
| โ
| โ
| As above, or if `T` is reference. |
/// | `&T`, `&mut T` | โ
| โ
| โ | Caller must uphold `&mut` guarantee! |
/// | `&self`, `&mut self` | โ
| โ
| โ | Caller must uphold `&mut self` guarantee! |
/// | `Async<Self>` | โ
| โ | โ
| Only inside `async` methods. |
/// | `Self` | โ | โ
| โ
| Valid as ctor rval, must be `ffi::Result<Self, E>`. |
/// | [`Wire<T>`](crate::wire::Wire) | โ
| โ
| โ
| Serialized transfer. |
/// | [`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`. |
/// | [`ffi::Vec<T>`](crate::pattern::vec::Vec), [`ffi::String`](crate::pattern::string::String) | โ
| โ
| โ
| Same as struct w.r.t. `T`. |
/// | [`ffi::Slice<T>`](crate::pattern::slice::Slice), [`ffi::SliceMut<T>`](crate::pattern::slice::SliceMut) | โ
| โ
| โ | Same as struct w.r.t. `T`. |
/// | [`ffi::CStrPtr`](crate::pattern::cstr::CStrPtr) | โ
| โ
| โ | Async can't accept pointers. |
///
/// <br>
///
/// In addition, the macro accepts the following options:
///
/// | 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.
///
/// # 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.
///
/// # 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>`. |
/// | `Option<T>` | โ
| โ
| โ
| โ
| As above. |
/// | `&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`. |
/// | [`ffi::Vec<T>`](crate::pattern::vec::Vec), [`ffi::String`](crate::pattern::string::String) | โ | โ | โ | โ | WIP, not yet supported. |
/// | [`ffi::Slice<T>`](crate::pattern::slice::Slice), [`ffi::SliceMut<T>`](crate::pattern::slice::SliceMut) | โ | โ | โ | โ | WIP, not yet supported. |
/// | [`ffi::CStrPtr`](crate::pattern::cstr::CStrPtr) | โ | โ | โ | โ | WIP, not yet supported. |
/// | `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.
///
/// # Instrumentation
///
/// Plugins come with built-in [`telemetry`](crate::telemetry) support and implement the
/// [`Metrics`](crate::telemetry::Metrics) trait. Since plugins may run on a
/// dynamic runtime such as the .NET CLR, they can introduce latency spikes caused by garbage
/// collection, JIT compilation, or other runtime activity. The telemetry integration lets you
/// observe call durations, helping diagnose performance issues.
///
/// Tick resolution is platform-dependent and works best for calls
/// longer than ~1 ยตs. If telemetry is disabled the overhead is a few CPU instructions. Synchronous
/// calls are instrumented end-to-end, `async` calls until `Future` readiness (i.e, the time
/// the future result can be successfully obtained via `.await`, not until you eventually do it).
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.