ffi_convert/
lib.rs

1//! A collection of utilities (traits, data structures, conversion functions, etc ...) to ease conversion between Rust and C-compatible data structures.
2//!
3//! Through two **conversion traits**, [`CReprOf`] and [`AsRust`], this crate provides a framework to convert idiomatic Rust structs to C-compatible structs that can pass through an FFI boundary, and conversely.
4//! They ensure that the developer uses best practices when performing the conversion in both directions (ownership-wise).
5//!
6//! The crate also provides a collection of useful utility functions and traits to perform conversions of types.
7//! It goes hand in hand with the `ffi-convert-derive` crate as it provides an **automatic derivation** of the [`CReprOf`] and [`AsRust`] trait.
8//!
9//! # Usage
10//! When dealing with an FFI frontier, the general philosophy of the crate is :  
11//! - When receiving pointers to structs created by C code, the struct is immediately converted to an owned (via a copy), idiomatic Rust struct through the use of the [`AsRust`] trait.
12//! - To send an idiomatic, owned Rust struct to C code, the struct is converted to C-compatible representation using the [`CReprOf`] trait.
13//!
14//! ## Example
15//!
16//! We want to be able to convert a **`Pizza`** Rust struct that has an idiomatic representation to a **`CPizza`** Rust struct that has a C-compatible representation in memory.
17//! We start by defining the fields of the `Pizza` struct :
18//! ```
19//! # struct Topping {};
20//! # struct Sauce {};
21//! pub struct Pizza {
22//!     pub name: String,
23//!     pub toppings: Vec<Topping>,
24//!     pub base: Option<Sauce>,
25//!     pub weight: f32,
26//! }
27//!```
28//!
29//! We then create the C-compatible struct by [mapping](#types-representations-mapping) idiomatic Rust types to C-compatible types :
30//! ```
31//! # use ffi_convert::CArray;
32//! # struct CTopping {};
33//! # struct CSauce {};
34//! #[repr(C)]
35//! pub struct CPizza {
36//!     pub name: *const libc::c_char,
37//!     pub toppings: *const CArray<CTopping>,
38//!     pub base: *const CSauce,
39//!     pub weight: libc::c_float,
40//! }
41//! ```
42//!
43//! This crate provides two traits that are useful for converting between Pizza to CPizza and conversely.
44//!
45//! ```ignore
46//!    CPizza::c_repr_of(pizza)
47//!      <=================|
48//!
49//! CPizza                   Pizza
50//!
51//!      |=================>
52//!       cpizza.as_rust()
53//!
54//! ```
55//! Instead of manually writing the body of the conversion traits, we can derive them :
56//!
57//! ```
58//! # use ffi_convert::{CReprOf, AsRust, CDrop, RawPointerConverter};
59//! # use ffi_convert::CArray;
60//! # use ffi_convert::RawBorrow;
61//! # struct Topping {};
62//! # #[derive(CReprOf, AsRust, CDrop)]
63//! # #[target_type(Topping)]
64//! # struct CTopping {};
65//! #
66//! # struct Pizza {
67//! #     name: String,
68//! #     toppings: Vec<Topping>,
69//! #     base: Option<Sauce>,
70//! #     weight: f32
71//! # };
72//! use libc::{c_char, c_float};
73//!
74//! struct Sauce {};
75//! #[derive(CReprOf, AsRust, CDrop, RawPointerConverter)]
76//! #[target_type(Sauce)]
77//! struct CSauce {};
78//!
79//! #[repr(C)]
80//! #[derive(CReprOf, AsRust, CDrop)]
81//! #[target_type(Pizza)]
82//! pub struct CPizza {
83//!     pub name: *const c_char,
84//!     pub toppings: *const CArray<CTopping>,
85//!     #[nullable]
86//!     pub base: *const CSauce,
87//!     pub weight: c_float,
88//! }
89//! ```
90//!
91//! You may have noticed that you have to derive two traits : the CDrop trait and the RawPointerConverter trait.
92//!
93//! The CDrop trait needs to be implemented on every C-compatible struct that require manual resource management.
94//! The release of those resources should be done in the drop method of the CDrop trait.
95//!
96//! The RawPointerConverter trait is implemented to perform the conversion of a C-like struct to a raw-pointer to this C-like structure (and conversely).
97//! Here, it is used behind the scene to convert a `CSauce` struct to a pointer to a raw pointer to CSause struct : `*const CSauce`
98//! (needed behind the scenes when the [`CReprOf`] trait is derived for `CPizza`).
99//!
100//! You can now pass the `CPizza` struct through your FFI boundary !
101//!
102
103//! ## Types representations mapping
104//!
105//! `T : CReprOf<U> + AsRust<U>`
106//! <table>
107//!     <thead>
108//!         <tr>
109//!             <th>C type</th>
110//!             <th>Rust type</th>
111//!             <th>C-compatible Rust type</th>
112//!         </tr>
113//!     </thead>
114//!     <tbody>
115//!         <tr>
116//!             <td><code>const char*</code></td>
117//!             <td><code>String</code></td>
118//!             <td><code>*const libc::c_char</code></td>
119//!         </tr>
120//!         <tr>
121//!             <td><code>const T*</code></td>
122//!             <td><code>U</code></td>
123//!             <td><code>*const T</code></td>
124//!         </tr>
125//!         <tr>
126//!             <td><code>T*</code></td>
127//!             <td><code>U</code></td>
128//!             <td><code>*mut T</code></td>
129//!         </tr>
130//!         <tr>
131//!             <td><code>T</code></td>
132//!             <td><code>U</code></td>
133//!             <td><code>T</code></td>
134//!         </tr>
135//!         <tr>
136//!             <td><code>const T*</code></td>
137//!             <td><code>Option&lt;U&gt;</code></td>
138//!             <td><code>*const T</code> (with <code>#[nullable]</code> field annotation)</td>
139//!         </tr>
140//!         <tr>
141//!             <td><code>CArrayT</code></td>
142//!             <td><code>Vec&lt;U&gt;</code></td>
143//!             <td><code>CArray&lt;T&gt;</code></td>
144//!         </tr>
145//!         <tr>
146//!             <td><code>CStringArray</code></td>
147//!             <td><code>Vec&lt;String&gt;</code></td>
148//!             <td><code>CStringArray</code></td>
149//!         </tr>
150//!         <tr>
151//!             <td><code>CRangeT</code></td>
152//!             <td><code>Range&lt;U&gt;</code></td>
153//!             <td><code>CRange&lt;T&gt;</code></td>
154//!         </tr>
155//!     </tbody>
156//! </table>
157//!
158
159//! ## The CReprOf trait
160
161//! The `CReprOf` trait allows to create a C-compatible representation of the reciprocal idiomatic Rust struct by consuming the latter.
162
163//! ```
164//! # use ffi_convert::{CReprOfError, CDrop};
165//! pub trait CReprOf<T>: Sized + CDrop {
166//!     fn c_repr_of(input: T) -> Result<Self, CReprOfError>;
167//! }
168//! ```
169
170//! This shows that the struct implementing it is a `repr(C)` compatible view of the parametrized
171//! type and can be created from an object of this type.
172
173//! ## The AsRust trait
174
175//! > When trying to convert a `repr(C)` struct that originated from C, the philosophy is to immediately convert
176//! > the struct to an **owned** idiomatic representation of the struct via the AsRust trait.
177
178//! The [`AsRust`] trait allows to create an idiomatic Rust struct from a C-compatible struct :
179
180//! ```
181//! # use ffi_convert::{AsRustError, CDrop};
182//! pub trait AsRust<T> {
183//!     fn as_rust(&self) -> Result<T, AsRustError>;
184//! }
185//! ```
186
187//! This shows that the struct implementing it is a `repr(C)` compatible view of the parametrized
188//! type and that an instance of the parametrized type can be created from this struct.
189
190//! ## The CDrop trait
191
192//! A Trait showing that the `repr(C)` compatible view implementing it can free up its part of memory that are not
193//! managed by Rust drop mechanism.
194
195//! ## The RawPointerConverter trait
196
197//! This trait completes the conversion traits toolbox provided by this crate : It expresses the
198//! conversion of a C-like struct to a raw pointer to this struct and conversely.
199//!
200//! This conversion trait comes in handy for C-like struct that have fields that points to other structs.
201
202pub use ffi_convert_derive::*;
203
204mod conversions;
205mod types;
206
207pub use conversions::*;
208pub use types::*;