hs_bindgen_traits/lib.rs
1//! # `hs-bingen-traits`
2//!
3//! Utility traits behind [`hs-bindgen`](https://github.com/yvan-sraka/hs-bindgen)
4//! ergonomics. It helps user to easily define wrapper function to derive a Rust
5//! type from and into a C-FFI safe target type (that match the memory layout of
6//! an Haskell type).
7//!
8//! ## What's this library for?
9//!
10//! [Does `repr(C)` define a trait I can use to check structs were declared with `#repr(C)`?](https://users.rust-lang.org/t/16323)
11//! The answer is sadly no ... that's what this library trying to provide, like
12//! what [`safer_ffi`](https://docs.rs/safer-ffi/latest/safer_ffi/layout/trait.ReprC.html)
13//! does, but in a simpler and more minimal way, since the goal here is only to
14//! target Haskell FFI.
15//!
16//! ## Acknowledgments
17//!
18//! ⚠️ This is still a working experiment, not yet production ready.
19//!
20//! This project was part of a work assignment as an
21//! [IOG](https://github.com/input-output-hk) contractor.
22//!
23//! ## License
24//!
25//! Licensed under either of [Apache License](LICENSE-APACHE), Version 2.0 or
26//! [MIT license](LICENSE-MIT) at your option.
27//!
28//! Unless you explicitly state otherwise, any contribution intentionally submitted
29//! for inclusion in this project by you, as defined in the Apache-2.0 license,
30//! shall be dual licensed as above, without any additional terms or conditions.
31
32#![cfg_attr(not(feature = "std"), no_std)]
33#![cfg_attr(not(feature = "std"), forbid(unsafe_code))]
34
35#[cfg(feature = "std")]
36mod fun;
37#[cfg(feature = "std")]
38mod str;
39#[cfg(feature = "std")]
40mod vec;
41#[cfg(feature = "std")]
42pub use self::{str::*, vec::*};
43
44/// Generate C-FFI cast from a given Rust type.
45///
46/// `impl FromReprC<Foo> for Bar` -> means `from` Rust `Foo` type into C `Bar` repr
47pub trait FromReprC<T>: private::CFFISafe {
48 #[must_use]
49 fn from(_: T) -> Self;
50}
51
52/// `impl IntoReprC<Foo> for Bar` -> means `from` C `Foo` type into Rust `Bar` repr
53pub trait IntoReprC<T> {
54 #[must_use]
55 fn into(self) -> T;
56}
57
58impl<T, U> IntoReprC<U> for T
59where
60 U: FromReprC<T>,
61 T: private::CFFISafe,
62{
63 #[inline]
64 fn into(self) -> U {
65 U::from(self)
66 }
67}
68
69/// Generate safe Rust wrapper from a given C-FFI type.
70///
71/// `impl FromReprRust<Foo> for Bar` -> means `from` C `Foo` type into Rust `Bar` repr
72pub trait FromReprRust<T: private::CFFISafe> {
73 #[must_use]
74 fn from(_: T) -> Self;
75}
76
77/// `impl IntoReprRust<Foo> for Bar` -> means `from` Rust `Foo` type into C `Bar` repr
78pub trait IntoReprRust<T> {
79 #[must_use]
80 fn into(self) -> T;
81}
82
83impl<T, U> IntoReprRust<U> for T
84where
85 U: FromReprRust<T>,
86 T: private::CFFISafe,
87{
88 fn into(self) -> U {
89 U::from(self)
90 }
91}
92
93mod private {
94 /// The trait `CFFISafe` is sealed and cannot be implemented for types outside this crate.
95 /// c.f. https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
96 pub trait CFFISafe {}
97
98 macro_rules! c_ffi_safe {
99 ($($ty:ty),*) => {$(
100 impl CFFISafe for $ty {}
101 // `*const T` is C-FFI safe if `T` is C-FFI safe
102 impl CFFISafe for *const $ty {}
103 )*};
104 }
105
106 // C-FFI safe types (the previous macro avoid redundant code)
107 c_ffi_safe![(), i8, i16, i32, i64, u8, u16, u32, u64, f32, f64];
108
109 macro_rules! c_ffi_safe_fun {
110 () => {
111 impl<Output: CFFISafe> CFFISafe for unsafe extern "C" fn() -> Output {}
112 };
113 ($x:ident $(,$xs:ident)*) => {
114 c_ffi_safe_fun!($( $xs ),*);
115 impl<$x $(,$xs)*, Output> CFFISafe for unsafe extern "C" fn($x, $($xs),*) -> Output
116 where
117 Output: CFFISafe,
118 $x: CFFISafe,
119 $($xs: CFFISafe),
120 * {}
121 };
122 }
123
124 c_ffi_safe_fun!(A, B, C, D, E, F);
125}
126
127macro_rules! transparent {
128 ($($ty:ty),*) => {$(
129 impl FromReprRust<$ty> for $ty {
130 #[inline]
131 fn from(x: $ty) -> Self { x }
132 }
133 impl FromReprC<$ty> for $ty {
134 #[inline]
135 fn from(x: $ty) -> Self { x }
136 }
137
138 impl FromReprRust<*const $ty> for *const $ty {
139 #[inline]
140 fn from(x: *const $ty) -> Self { x }
141 }
142 impl FromReprC<*const $ty> for *const $ty {
143 #[inline]
144 fn from(x: *const $ty) -> Self { x }
145 }
146 )*};
147}
148
149// C-FFI safe type trivially implement both traits
150transparent![i8, i16, i32, i64, u8, u16, u32, u64, f32, f64];
151
152/// This is used by Rust function that doesn’t return any value
153/// (`void` C equivalent).
154impl FromReprC<()> for () {
155 #[inline]
156 fn from(_: ()) -> Self {}
157}
158
159impl<T> FromReprRust<*const T> for *mut T
160where
161 *const T: private::CFFISafe,
162{
163 #[inline]
164 fn from(x: *const T) -> Self {
165 x as *mut T
166 }
167}