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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::string::rust_string_to_c;
use std::os::raw::c_char;

/// Implements [`IntoFfi`] for the provided types (more than one may be passed in) by allocating
/// `$T` on the heap as an opaque pointer.
///
/// This is typically going to be used from the "Rust component", and not the "FFI component" (see
/// the top level crate documentation for more information), however you will still need to
/// implement a destructor in the FFI component using [`define_box_destructor!`].
#[macro_export]
macro_rules! implement_into_ffi_by_pointer {
    ($($T:ty),* $(,)*) => {$(
        unsafe impl $crate::IntoFfi for $T {
            type Value = *mut $T;

            #[inline]
            fn ffi_default() -> *mut $T {
                ::std::ptr::null_mut()
            }

            #[inline]
            fn into_ffi_value(self) -> *mut $T {
                ::std::boxed::Box::into_raw(::std::boxed::Box::new(self))
            }
        }
    )*}
}

/// Implements [`IntoFfi`] for the provided types (more than one may be passed in) by converting to
/// the type to a JSON string. This macro also allows you to return `Vec<T>` for the types, also by
/// serialization to JSON (by way of [`IntoFfiJsonTag`]).
///
/// This is typically going to be used from the "Rust component", and not the "FFI component" (see
/// the top level crate documentation for more information).
///
/// Note: Each type passed in must implement or derive `serde::Serialize`.
///
/// ## Panics
///
/// The [`IntoFfi`] implementation this macro generates may panic in the following cases:
///
/// - You've passed a type that contains a Map that has non-string keys (which can't be represented
///   in JSON).
///
/// - You've passed a type which has a custom serializer, and the custom serializer failed.
///
/// These cases are both rare enough that this still seems fine for the majority of uses.
#[macro_export]
macro_rules! implement_into_ffi_by_json {
    ($($T:ty),* $(,)*) => {$(
        unsafe impl $crate::IntoFfi for $T {
            type Value = *mut ::std::os::raw::c_char;
            #[inline]
            fn ffi_default() -> *mut ::std::os::raw::c_char {
                ::std::ptr::null_mut()
            }
            #[inline]
            fn into_ffi_value(self) -> *mut ::std::os::raw::c_char {
                $crate::convert_to_json_string(&self)
            }
        }

        impl $crate::IntoFfiJsonTag for $T {}
    )*}
}

/// For a number of reasons (name collisions are a big one, but, it also wouldn't work on all
/// platforms), we cannot export `extern "C"` functions from this library. However, it's pretty
/// common to want to free strings allocated by rust, so many libraries will need this, so we
/// provide it as a macro.
///
/// It simply expands to a `#[no_mangle] pub unsafe extern "C" fn` which wraps this crate's
/// [`destroy_c_string`] function.
///
/// ## Caveats
///
/// If you're using multiple separately compiled rust libraries in your application, it's critical
/// that you are careful to only ever free strings allocated by a Rust library using the same rust
/// library. Passing them to a different Rust library's string destructor will cause you to corrupt
/// multiple heaps.
///
/// Additionally, be sure that all strings you pass to this were actually allocated by rust. It's a
/// common issue for JNA code to transparently convert Pointers to things to Strings behind the
/// scenes, which is quite risky here. (To avoid this in JNA, only use `String` for passing
/// read-only strings into Rust, e.g. it's for passing `*const c_char`. All other uses should use
/// `Pointer` and `getString()`).
///
/// Finally, to avoid name collisions, it is strongly recommended that you provide an name for this
/// function unique to your library.
///
/// ## Example
///
/// ```rust
/// # use ffi_support::define_string_destructor;
/// define_string_destructor!(mylib_destroy_string);
/// ```
#[macro_export]
macro_rules! define_string_destructor {
    ($mylib_destroy_string:ident) => {
        #[doc = "Public destructor for strings managed by the other side of the FFI."]
        #[no_mangle]
        pub unsafe extern "C" fn $mylib_destroy_string(s: *mut ::std::os::raw::c_char) {
            if !s.is_null() {
                $crate::destroy_c_string(s)
            }
        }
    };
}

/// Define a (public) destructor for a type that was allocated by `Box::into_raw(Box::new(value))`
/// (e.g. a pointer which is probably opaque).
///
/// ## Caveats
///
/// This can go wrong in a ridiculous number of ways, and we can't really prevent any of them. But
/// essentially, the caller (on the other side of the FFI) needs to be extremely careful to ensure
/// that it stops using the pointer after it's freed.
///
/// Also, to avoid name collisions, it is strongly recommended that you provide an name for this
/// function unique to your library. (This is true for all functions you expose).
///
/// ## Example
///
/// ```rust
/// # use ffi_support::define_box_destructor;
/// struct CoolType(Vec<i32>);
///
/// define_box_destructor!(CoolType, mylib_destroy_cooltype);
/// ```
#[macro_export]
macro_rules! define_box_destructor {
    ($T:ty, $destructor_name:ident) => {
        #[no_mangle]
        pub unsafe extern "C" fn $destructor_name(v: *mut $T) {
            if !v.is_null() {
                drop(::std::boxed::Box::from_raw(v))
            }
        }
    };
}

// Needs to be pub so the macro can call it, but that's all.
#[doc(hidden)]
pub fn convert_to_json_string<T: serde::Serialize>(value: &T) -> *mut c_char {
    // This panic is inside our catch_panic, so it should be fine. We've also documented the case
    // where the IntoFfi impl that calls this panics, and it's rare enough that it shouldn't matter
    // that if it happens we return an ExternError representing a panic instead of one of some other
    // type (especially given that the application isn't likely to be able to meaningfully handle
    // JSON serialization failure).
    let as_string = serde_json::to_string(&value).unwrap();
    rust_string_to_c(as_string)
}