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
//! Traits and helpers to convert between idiomatic Rust values and C-compatible
//! representations when crossing an FFI boundary.
//!
//! The crate is built around two conversion traits, [`CReprOf`] and [`AsRust`],
//! and two supporting traits, [`CDrop`] and [`RawPointerConverter`]. Derive
//! macros for all four are provided by the companion
//! [`ffi-convert-derive`](https://docs.rs/ffi-convert-derive) crate and
//! re-exported here.
//!
//! Common containers (arrays, string arrays, ranges) live in the separate
//! [`ffi-convert-extra-ctypes`](https://docs.rs/ffi-convert-extra-ctypes)
//! crate and can be pulled in on demand.
//!
//! # Philosophy
//!
//! `ffi-convert`'s memory-management model makes as few assumptions as
//! possible about how the C side allocates, holds, or frees memory.
//!
//! Two traits cover the two directions across the FFI boundary:
//!
//! - **Incoming from C** — [`AsRust`] takes a `&CFoo` and returns an owned
//! `Foo` built by deep-copying every field. It is a defensive copy: once
//! `as_rust` returns, the resulting Rust value does not reference any
//! C-owned memory, and nothing else in the crate reads from the original
//! pointer afterwards. The C caller is free to keep, reuse, or release
//! the pointer however its own rules require.
//! - **Outgoing to C** — [`CReprOf`] consumes a `Foo` and produces a `CFoo`
//! that owns any heap memory its pointer fields reference. The `CFoo` is
//! then handed to C as a raw pointer; to release everything, C sends the
//! pointer back to Rust through a `free`-style FFI function that lets the
//! value drop (releasing its pointer fields via [`CDrop`]).
//!
//! ```text
//! CPizza::c_repr_of(pizza)
//! ┌───────────────────────────────┐
//! │ ▼
//! ┌──────────┐ ┌──────────┐
//! │ Pizza │ │ CPizza │
//! │ (Rust) │ │ (C) │
//! └──────────┘ └──────────┘
//! ▲ │
//! └───────────────────────────────┘
//! c_pizza.as_rust()
//! ```
//!
//! # Quick example
//!
//! Define the Rust type you want to expose, then define a `#[repr(C)]` mirror
//! and derive the conversion traits. The mirror's fields use C-compatible
//! types (see [the mapping table](#type-mapping)).
//!
//! ```
//! use ffi_convert::{AsRust, CDrop, CReprOf, RawBorrow, RawPointerConverter};
//! use std::ffi::{c_char, c_float};
//!
//! pub struct Sauce {
//! pub spiciness: f32,
//! }
//!
//! #[repr(C)]
//! #[derive(CReprOf, AsRust, CDrop, RawPointerConverter)]
//! #[target_type(Sauce)]
//! pub struct CSauce {
//! pub spiciness: c_float,
//! }
//!
//! pub struct Pizza {
//! pub name: String,
//! pub base: Option<Sauce>,
//! pub weight: f32,
//! }
//!
//! #[repr(C)]
//! #[derive(CReprOf, AsRust, CDrop, RawPointerConverter)]
//! #[target_type(Pizza)]
//! pub struct CPizza {
//! pub name: *const c_char,
//! #[nullable]
//! pub base: *const CSauce,
//! pub weight: c_float,
//! }
//! ```
//!
//! Two things to notice:
//!
//! - `CSauce` derives [`RawPointerConverter`] because `CPizza::base` stores a
//! `*const CSauce`; `CPizza` derives it too so it can itself be handed to C
//! as a `*const CPizza`. In both cases the derived [`CReprOf`] turns a value
//! into a raw pointer via `into_raw_pointer`.
//! - `CPizza::base` carries `#[nullable]` because the Rust field is
//! `Option<Sauce>`. The attribute tells the derives to map `None` to a null
//! pointer on the way out and a null pointer to `None` on the way back.
//!
//! With the derives in place, let's write an FFI wrapper with three small functions —
//! one to read a C-owned value, one to hand a Rust value to C, and one to free
//! it:
//!
//! ```
//! # use ffi_convert::{AsRust, CDrop, CReprOf, RawBorrow, RawPointerConverter};
//! # use std::ffi::{c_char, c_float};
//! # pub struct Sauce { pub spiciness: f32 }
//! # #[repr(C)]
//! # #[derive(CReprOf, AsRust, CDrop, RawPointerConverter)]
//! # #[target_type(Sauce)]
//! # pub struct CSauce { pub spiciness: c_float }
//! # pub struct Pizza {
//! # pub name: String,
//! # pub base: Option<Sauce>,
//! # pub weight: f32,
//! # }
//! # #[repr(C)]
//! # #[derive(CReprOf, AsRust, CDrop, RawPointerConverter)]
//! # #[target_type(Pizza)]
//! # pub struct CPizza {
//! # pub name: *const c_char,
//! # #[nullable]
//! # pub base: *const CSauce,
//! # pub weight: c_float,
//! # }
//! // Read a CPizza handed to us by C: deep-copy its contents into an owned
//! // Rust `Pizza`, then run whatever logic we need. The original pointer is
//! // untouched; C keeps ownership of it.
//! #[unsafe(no_mangle)]
//! pub unsafe extern "C" fn inspect_pizza(c_pizza: *const CPizza) {
//! let c_pizza = unsafe { CPizza::raw_borrow(c_pizza) }
//! .expect("c_pizza must not be null");
//! let pizza: Pizza = c_pizza.as_rust().expect("invalid CPizza contents");
//! println!("{} ({}g)", pizza.name, pizza.weight);
//! }
//!
//! // Build a Rust `Pizza`, convert it to `CPizza`, and hand C a raw pointer
//! // via [`RawPointerConverter::into_raw_pointer`]. The caller must
//! // eventually invoke `free_pizza` to release the allocation.
//! #[unsafe(no_mangle)]
//! pub extern "C" fn make_pizza() -> *const CPizza {
//! let pizza = Pizza {
//! name: "Margarita".to_owned(),
//! base: Some(Sauce { spiciness: 1.5 }),
//! weight: 450.0,
//! };
//! CPizza::c_repr_of(pizza)
//! .expect("pizza name contains an interior NUL byte")
//! .into_raw_pointer()
//! }
//!
//! // Reclaim a pointer produced by `make_pizza`.
//! // [`RawPointerConverter::drop_raw_pointer`] takes ownership back and
//! // drops the value, releasing every inner pointer field via [`CDrop`].
//! #[unsafe(no_mangle)]
//! pub unsafe extern "C" fn free_pizza(c_pizza: *const CPizza) {
//! let _ = unsafe { CPizza::drop_raw_pointer(c_pizza) };
//! }
//! ```
//!
//! # Type mapping
//!
//! `T: CReprOf<U> + AsRust<U>` — in the table below, `T` is the C-compatible
//! Rust type and `U` is the idiomatic Rust type.
//!
//! | C type | Rust type (`U`) | C-compatible Rust type (`T`) | Provided by |
//! |------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------|------------------------------|
//! | any scalar (`int`, …) | same scalar | same scalar | `ffi-convert` |
//! | `const char*` | `String` | `*const std::ffi::c_char` | `ffi-convert` |
//! | `const T*` | `U` | `*const T` | `ffi-convert` |
//! | `T*` | `U` | `*mut T` | `ffi-convert` |
//! | `const T*` (nullable) | `Option<U>` | `*const T` with `#[nullable]` | `ffi-convert` |
//! | `T[N]` | `[U; N]` | `[T; N]` | `ffi-convert` |
//! | `CArrayT` | `Vec<U>` | [`CArray<T>`](https://docs.rs/ffi-convert-extra-ctypes/latest/ffi_convert_extra_ctypes/struct.CArray.html) | `ffi-convert-extra-ctypes` |
//! | `CStringArray` | `Vec<String>` | [`CStringArray`](https://docs.rs/ffi-convert-extra-ctypes/latest/ffi_convert_extra_ctypes/struct.CStringArray.html) | `ffi-convert-extra-ctypes` |
//! | `CRangeT` | `Range<U>` | [`CRange<T>`](https://docs.rs/ffi-convert-extra-ctypes/latest/ffi_convert_extra_ctypes/struct.CRange.html) | `ffi-convert-extra-ctypes` |
//!
//! The derives accept both `*const T` and `*mut T` for any pointer row.
//!
//! # Traits at a glance
//!
//! | Trait | Direction | Purpose |
//! |--------------------------|----------------------|-------------------------------------------------------------------------------------------------------|
//! | [`CReprOf<U>`] | Rust → C | Consume an idiomatic Rust value and produce its C-compatible twin. |
//! | [`AsRust<U>`] | C → Rust | Produce an owned Rust value from a borrowed C-compatible value. |
//! | [`CDrop`] | cleanup | Free heap data owned by a C-compatible struct. |
//! | [`RawPointerConverter`] | pointer boxing | Box a value into `*const T` / `*mut T` and take it back. |
//! | [`RawBorrow`] | pointer borrowing | Borrow `&T` from a raw pointer without taking ownership. Returns an error if the pointer is null. |
//! | [`RawBorrowMut`] | pointer borrowing | Borrow `&mut T` from a raw pointer without taking ownership. Returns an error if the pointer is null. |
//!
//! [`CReprOf`], [`AsRust`], [`CDrop`], and [`RawPointerConverter`] all have
//! derive macros.
//!
//! # Deriving the traits
//!
//! The derives are the intended way to use the crate. Typical derive
//! combinations on a `#[repr(C)]` type are:
//!
//! - `#[derive(CReprOf, CDrop)]` for a type created in Rust and read from C
//! - `#[derive(AsRust)]` for a type created in C and read in Rust
//! - `#[derive(AsRust, CReprOf, CDrop)]` for a type created and read in C and Rust
//!
//! Deriving `CDrop` and `CReprOf` together is recommended: `CDrop` assumes raw
//! pointers were initialized the way the `CReprOf` derive initializes them.
//!
//! The derives expect:
//!
//! - `#[target_type(Path)]` on every struct or enum that derives [`CReprOf`]
//! or [`AsRust`], pointing at the idiomatic Rust type being mirrored.
//! - `#[nullable]` on every pointer field whose Rust counterpart is an
//! [`Option`]. The attribute is shared by all three derives: [`CReprOf`]
//! reads it to emit a null for `None`, [`AsRust`] to return `None` on a
//! null pointer, and [`CDrop`] to skip the free on null. A mismatch
//! between the Rust-side `Option<T>` and the C-side `#[nullable]` is a
//! compile error.
//! - [`RawPointerConverter`] to be implemented on any nested C-compatible
//! struct reached through a pointer field, usually by
//! `#[derive(RawPointerConverter)]`.
//!
//! The available attributes are:
//!
//! | Attribute | Applies to | Used by | Purpose |
//! |------------------------------------------|-------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
//! | `#[target_type(Path)]` | struct / enum | `CReprOf`, `AsRust` | The idiomatic Rust type this C-compatible type mirrors. |
//! | `#[no_drop_impl]` | struct / enum | `CDrop` | Only implement [`CDrop`]; skip the blanket [`Drop`] impl so you can write one manually. |
//! | `#[as_rust_extra_field(name = expr)]` | struct | `AsRust` | Initialize an extra field on the Rust side that has no C counterpart. Repeatable; `self` (the C-side value) is in scope inside `expr`. |
//! | `#[nullable]` | pointer field | `CReprOf`, `AsRust`, `CDrop`| Treat a `*const T` / `*mut T` as `Option<…>`. Required for every optional pointer field. |
//! | `#[target_name(ident)]` | field | `CReprOf`, `AsRust` | Name of the corresponding field on the Rust side when it differs from the C-side name. |
//! | `#[c_repr_of_convert(expr)]` | field | `CReprOf`, `AsRust` | Override the `CReprOf` conversion with a custom expression. The owned `input: TargetType` is in scope. The field is also skipped by `AsRust`. |
//!
//! ## Constraints
//!
//! - **C strings**: a field is recognized as a C string only when the
//! pointee's type name is literally `c_char` — `*const std::ffi::c_char`,
//! `*mut std::ffi::c_char`, and `*const c_char` all qualify. A `type` alias
//! for `c_char` is not recognized.
//! - **Multi-level pointer fields** (such as `*const *const CFoo`) are
//! accepted by the [`AsRust`] derive only when the field is also
//! `#[nullable]`.
//! - **Enums with data**:not supported. the derives accept enums only when
//! every variant is a unit variant.
//!
//! # Interop checklist
//!
//! A typical FFI-exposed function follows this pattern:
//!
//! 1. Receive a `*const CInput` from C and convert it with [`AsRust`], or
//! borrow it with [`RawBorrow`] if the C side keeps ownership.
//! 2. Run the Rust logic.
//! 3. Build a `COutput` with [`CReprOf`] and return it to C via
//! [`RawPointerConverter::into_raw_pointer`].
//! 4. Expose a `free`-style function that takes the pointer back with
//! [`RawPointerConverter::from_raw_pointer`] and lets the value drop.
pub use *;
pub use *;